13

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?
8
  • ameyalokare.com/rust/2017/10/12/rust-str-vs-String.html Seems like String coerces to str but not the other way around. Commented Feb 26, 2018 at 10:13
  • 3
    Vec::contains is too restrictive 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. Commented Feb 26, 2018 at 10:34
  • 2
    Possible duplicate of What are the differences between Rust's `String` and `str`? Commented Feb 26, 2018 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. Commented Feb 26, 2018 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. Commented Feb 26, 2018 at 11:17

3 Answers 3

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.

Sign up to request clarification or add additional context in comments.

6 Comments

It's true that you have worked around the issue, but that's not quite what I meant. Can you do it still using contains?
I'm afraid no, but what's the difference in your eyes? Both do the same :)
Practically you are right, but it doesn't help me understand the type system restrictions at play here.
Ok, but your question was: Is there a way to make the code work without the extra allocation? I answered that, I get a downvote? Okay :/ Go on
FWIW it wasn't me who downvoted. I'll revise my question to be more clear.
|
8

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.

Comments

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.

2 Comments

I don't think this quite answers my question. Both &str and &String are borrowed types.
@EddBarrett You are right. I hope the edited answer now answers your question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.