12

A friend asked me to explain the following quirk in Rust. I was unable to, hence this question:

fn main() {
    let l: Vec<String> = Vec::new();
    //let ret = l.contains(&String::from(func())); // works
    let ret = l.contains(func());  // does not work
    println!("ret: {}", ret);
}

fn func() -> & 'static str {
    "hello"
}

Example on the Rust Playground

The compiler will complain like this:

error[E0308]: mismatched types
 --> src/main.rs:4:26
  |
4 |     let ret = l.contains(func());  // does not work
  |                          ^^^^^^ expected struct `std::string::String`, found str
  |
  = note: expected type `&std::string::String`
             found type `&'static str`

In other words, &str does not coerce with &String.

At first I thought it was to do with 'static, however that is a red herring.

The commented line fixes the example at the cost of an extra allocation.

My questions:

  • Why doesn't &str coerce with &String?
  • Is there a way to make the call to contains work without the extra allocation?
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Edd Barrett
  • 3,425
  • 2
  • 29
  • 48
  • http://www.ameyalokare.com/rust/2017/10/12/rust-str-vs-String.html Seems like String coerces to str but not the other way around. – Laserallan Feb 26 '18 at 10:13
  • 3
    [Vec::contains is too restrictive](https://github.com/rust-lang/rust/issues/42671) is also relevant. The short of it is, fixing this so contains can make the comparable references is possible, but it broke other code. A very Rust gotcha. – Yann Vernier Feb 26 '18 at 10:34
  • 2
    Possible duplicate of [What are the differences between Rust's \`String\` and \`str\`?](https://stackoverflow.com/questions/24158114/what-are-the-differences-between-rusts-string-and-str) – Nikolay Lebedev Feb 26 '18 at 10:53
  • I think it's more subtle than that. We know the difference between `String` and `str`. It's a question of coercion. – Edd Barrett Feb 26 '18 at 11:00
  • @EddBarrett I think that my answer clearly clarifies why it does not make sense for `str` to coerce to `String`, and also how you could change your code to avoid allocation. – Marko Popovic Feb 26 '18 at 11:17
  • *I don't think the possible duplicate answers my question* — if you spend more time having a useful, distinctive title, false duplicates will be less common. – Shepmaster Feb 26 '18 at 14:43
  • Note that [the function has nothing to do with the problem](https://play.rust-lang.org/?gist=b65a8ea4d0c788f8c861503a6ce11001&version=stable). – Shepmaster Feb 26 '18 at 14:47
  • We've just found https://github.com/rust-lang/rust/issues/42671 – Edd Barrett Feb 26 '18 at 14:58

3 Answers3

11

Your first question should be answer already by @Marko.

Your second question, should be easy to answer as well, just use a closure:

let ret = l.iter().any(|x| x == func());

Edit:

Not the "real" answer anymore, but I let this here for people who might be interested in a solution for this.

hellow
  • 12,430
  • 7
  • 56
  • 79
7

It seems that the Rust developers intend to adjust the signature of contains to allow the example posted above to work.

In some sense, this is a known bug in contains. It sounds like the fix won't allow those types to coerce, but will allow the above example to work.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Edd Barrett
  • 3,425
  • 2
  • 29
  • 48
3

std::string::String is a growable, heap-allocated data structure whereas string slice (str) is an immutable fixed-length string somewhere in memory. String slice is used as a borrowed type, via &str. Consider it as view to some string date that resides somewhere in memory. That is why it does not make sense for str to coerce to String, while the other way around perfectly makes sense. You have a heap-allocated String somewhere in memory and you want to use a view (a string slice) to that string.

To answer your second question. There is no way to make the code work in the current form. You either need to change to a vector of string slices (that way, there will be no extra allocation) or use something other then contains method.

Marko Popovic
  • 3,999
  • 3
  • 22
  • 37