rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
97.98k stars 12.68k forks source link

Deref coercion from `String` to `&str` doesn't seem to always work #22649

Open Valloric opened 9 years ago

Valloric commented 9 years ago

This code compiles fine:

fn main() {
  let x = "a".to_string();
  let y: String = ["b", &x[..], "c"].concat();
  println!("{}", y);
}

but this code doesn't

fn main() {
  let x = "a".to_string();
  // Doesn't work
  let y: String = ["b", &x, "c"].concat();
  println!("{}", y);
}

error output:

<anon>:4:25: 4:27 error: mismatched types:
 expected `&str`,
    found `&collections::string::String`
(expected str,
    found struct `collections::string::String`) [E0308]
<anon>:4   let y: String = ["b", &x, "c"].concat();
                                 ^~

I'd expect to not have to sometimes write &foo[..] and sometimes &foo to get a &str out a String. The longer form is too verbose, and the inconsistency of having to sometimes use one over the other seems like a needless user mental model cost, especially because it's not obvious to me at all when I should use one form over the other.

rustc version: rustc 1.0.0-nightly (522d09dfe 2015-02-19) (built 2015-02-21)

japaric commented 9 years ago

cc @eddyb

Valloric commented 9 years ago

Another example, with Vector<T> and [T]. This works:

fn main() {
  let x = vec![97u8];
  assert_eq!(b"a", &x[..]);
}

But this fails to compile:

fn main() {
  let x = vec![97u8];
  assert_eq!(b"a", &x);
}

Output:

<std macros>:5:10: 5:35 error: the trait `core::cmp::PartialEq<collections::vec::Vec<u8>>` is not implemented for the type `[u8]` [E0277]
<std macros>:5 if ! ( ( * left_val == * right_val ) && ( * right_val == * left_val ) ) {
                        ^~~~~~~~~~~~~~~~~~~~~~~~~
<std macros>:1:1: 9:39 note: in expansion of assert_eq!
<anon>:3:3: 3:24 note: expansion site
<std macros>:5:43: 5:68 error: the trait `core::cmp::PartialEq<[u8]>` is not implemented for the type `collections::vec::Vec<u8>` [E0277]
<std macros>:5 if ! ( ( * left_val == * right_val ) && ( * right_val == * left_val ) ) {
                                                         ^~~~~~~~~~~~~~~~~~~~~~~~~
<std macros>:1:1: 9:39 note: in expansion of assert_eq!
<anon>:3:3: 3:24 note: expansion site
error: aborting due to 2 previous errors
playpen: application terminated with error code 101

I have tons of other examples in my codebase. Like, 30+.

japaric commented 9 years ago

(FWIW, that second example could be written as assert_eq!(b"a", vec![97u8]) because PartialEq is implemented between &[u8] and Vec<u8>, no need for deref coercions nor slicing syntax)

yann-ledu commented 9 years ago

The problem also appears here:

fn main()
{
    let rs = "toto";
    let s = rs.to_string();

    // That works as expected, &s coerces s to type &str
    fn titi(s:&str) { assert_eq!(s,"toto");};
    titi(&s);

    // That also works, same coercion happens
    let cs:&str = &s;
    assert_eq!(cs,rs);

    // The following expression fails to run, as though the coercion did not happen
    let cs = &s;
    assert_eq!(cs,rs);
}
huonw commented 8 years ago

This is somewhat similar to https://github.com/rust-lang/rust/issues/16864.

I think @nrc was doing some work on coercions recently?

Valloric commented 8 years ago

I can confirm the issue is still present on rustc 1.8.0-nightly (57c357d89 2016-02-16). My testcase still repros.

eddyb commented 8 years ago

Both arrays and the expansion of assert_eq fail to trigger coercions. The former is easier to fix, try changing this call to be to check_expr_coercable_to_type instead. That should make it so the first array element with even a partially inferrable type triggers coercions on subsequent array elements. The perfect solution would involve smarter unification such that &String followed by &str goes back and coerces the &String (or @nikomatsakis' order-independent type-checking).

Valloric commented 8 years ago

Interesting developments: the first test case I posted now compiles fine.

fn main() {
  let x = "a".to_string();
  // Doesn't work
  let y: String = ["b", &x, "c"].concat();
  println!("{}", y);
}

But the second test case fails compilation:

fn main() {
  let x = vec![97u8];
  assert_eq!(b"a", &x);
}

Here's the error message:

error[E0277]: the trait bound `[u8; 1]: std::cmp::PartialEq<std::vec::Vec<u8>>` is not satisfied
 --> <std macros>:5:6
  |
5 | if ! ( * left_val == * right_val ) {
  |      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
foo.rs:3:3: 3:24 note: in this expansion of assert_eq! (defined in <std macros>)
  |
  = help: the following implementations were found:
  = help:   <[A; 0] as std::cmp::PartialEq<[B; 0]>>
  = help:   <[A; 0] as std::cmp::PartialEq<[B]>>
  = help:   <[B] as std::cmp::PartialEq<[A; 0]>>
  = help:   <[A; 0] as std::cmp::PartialEq<&'b [B]>>
  = help: and 228 others
  = note: required because of the requirements on the impl of `std::cmp::PartialEq<&std::vec::Vec<u8>>` for `&[u8; 1]`

error: aborting due to previous error

rustc version: rustc 1.12.0 (3191fbae9 2016-09-23).

eddyb commented 8 years ago

such that &String followed by &str goes back and coerces the &String

FWIW this is what was implemented and why the array case works now.

PSeitz commented 6 years ago

Here is another case with a trait, wich fails to compile:


fn main() {

    concat("nice", "cooel");
    concat("nice".to_string(), "cooel");
    let yop = "nice".to_string();
    concat(&yop, "cooel"); // need to add manually "as &str"

}

pub fn concat<S: Into<String>>(path: S, suffix: &str) -> String {
    path.into() + suffix
}
xixixao commented 5 years ago

Also ran into this, in a very simple case:

works:

fn prompt_and_validate(msg: &str, options: &[&str]) {
  let reply = prompt(msg);
  let r: &str = &reply;
  if options.contains(&r) {

  }
}

doesn't work:

fn prompt_and_validate(msg: &str, options: &[&str]) {
  let reply = prompt(msg);
  let r = &reply;
  if options.contains(&r) {

  }
}

or in other words, double deref is not working automatically:

fn prompt_and_validate(msg: &str, options: &[&str]) {
  let reply = prompt(msg);
  if options.contains(&&reply) {

  }
}

with

mismatched types

expected str, found struct `std::string::String`

note: expected type `&&str`
         found type `&&std::string::String`

(workaround is &reply.as_str())

rsalmei commented 3 years ago

This still seems to fail today in Rust 1.55:

fn main() {
    let cmd = "some command".split_whitespace().collect::<Vec<_>>();
    match &cmd {
        ["some", sub] => println!("some {}", sub),
        ["quit"] => println!("bye!"),
        _ => println!("oops"),
    }
}

with:

error[E0529]: expected an array or slice, found `Vec<&str>`
  --> src/main.rs:13:9
   |
13 |         ["some", sub] => println!("some {}", sub),
   |         ^^^^^^^^^^^^^ pattern cannot match with input type `Vec<&str>`

error[E0529]: expected an array or slice, found `Vec<&str>`
  --> src/main.rs:14:9
   |
14 |         ["quit"] => println!("bye!"),
   |         ^^^^^^^^ pattern cannot match with input type `Vec<&str>`

For more information about this error, try `rustc --explain E0529`.
error: could not compile `playground` due to 2 previous errors

Exchanging &cmd for &*cmd or &cmd[..] makes it work. Is this intended or really a bug that the coercion does not trigger here?

eddyb commented 3 years ago

It's a missing feature: we could try to figure out some kind of "type skeleton" from the patterns, before type-checking the expression being matched, but we don't today.

That is, today the information flows only the other way around: from the expression to the patterns.

pierwill commented 1 year ago

we could try to figure out some kind of "type skeleton" from the patterns, before type-checking the expression being matched, but we don't today

@eddyb Is this still something we might want to do?