3

I am wondering why this works

fn main() {
    let v: Vec<&str> = Vec::new();
    let s: &str = v.get(0).unwrap();
    let new_string = String::from(s);
    println!("{}", new_string);
}

but not this one:

fn main() {
    let v: Vec<&str> = Vec::new();
    let s = v.get(0).unwrap();
    let new_string = String::from(s);
    println!("{}", new_string);
}

I get the following error from the second snippet:

let new_string = String::from(s);
                  ^^^^^^^^^^^^ the trait `std::convert::From<&&str>` is not implemented for `std::string::String`
otm
  • 685
  • 2
  • 9
  • 21
  • Not exact duplicates but see [Rust HashMap: Why do I need a double ampersand?](https://stackoverflow.com/questions/54982977/rust-hashmap-why-do-i-need-a-double-ampersand) and [why is “&&” being used here?](https://stackoverflow.com/questions/43828013/why-is-being-used-here) – mcarton Jun 27 '19 at 21:37
  • Not familiar with rust. If it has a `type()` function or something similar I would print that out for `s` in both the snippets above and compare. I suspect it has to do with the double reference (`&&str`) shown in the error. – Perplexabot Jun 27 '19 at 21:38
  • 1
    TL;DR: you have a vector of references (`T == &str`), `get` returns an optional reference (`Option<&T>`), which means you got two references (`Option<&&str>`). – mcarton Jun 27 '19 at 21:39
  • https://play.integer32.com/?version=stable&mode=debug&edition=2018&gist=a1640110af6b89d85daa5ad655f97663 – Stargateur Jun 27 '19 at 22:08

1 Answers1

0

The Vec::<T>::get() method returns an Option<&T>, that is, an option of a reference into the vector. Because your vector already contains references (&str), it means that get() in your case returns Option<&&str>.

Now, the reason why your first example compiles is that by being explicit about the type of the s variable you cause deref coercion to fire, that is, the compiler will automatically insert a dereference operator:

// You write this:
let s: &str = v.get(0).unwrap();

// Compiler actually does this:
let s: &str = *v.get(0).unwrap();

Then, because there exists an implementation of the From<&str> trait for String, the compiler accepts the String::from(s) call.

In the second piece, however, without an explicit type annotation the type assigned to s is &&str:

let s: &&str = v.get(0).unwrap();

This changes the situation with the String::from() method: there is no From<&&str> implementation for String, and the compiler fails to compile the code.

A logical question here would be why no deref coercion happens in this case, when s is used as an argument for from()? The answer is that deref coercion does not happen for generic methods, and String::from is a generic method because it relies on the type parameter of the From trait. The compiler can't do a deref coercion, because it is totally possible (in principle) to have both From<&str> and From<&&str> methods which would potentially do different things.

Even if From<&&str> does not exist now, and the compiler would have applied a deref coercion, it would make the code brittle against future evolution: if for some reason From<&&str> is added in a future version of the standard library, your code may silently break, because it will now call a different method than before, with potentially different logic.

(Naturally, I'm not saying that From<&&str> would indeed be added in the future libstd version; this is generic reasoning applicable to any kind of trait, in any code base).

Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296