Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

&&str.to_owned() doesn't result in a String

Tags:

reference

rust

I've got the following code:

use std::collections::HashMap;
fn main() {
    let xs: Vec<&str> = vec!("a", "b", "c", "d");
    let ys: Vec<i32> = vec!(1, 2, 3, 4);

    let mut map: HashMap<String,i32> = HashMap::new();
    for (x,y) in xs.iter().zip(ys) {
        map.insert(x.to_owned(), y);
    }
    println!("{:?}", map);
}

Which results in error:

<anon>:8:20: 8:32 error: mismatched types:
 expected `collections::string::String`,
    found `&str`
(expected struct `collections::string::String`,
    found &-ptr) [E0308]
<anon>:8         map.insert(x.to_owned(), y);

But it doesn't make sense to me. x should be &&str at this point. So why doesn't &&str.to_owned() automagically Deref the same way x.to_string() does at this point? (Why is x.to_owned() a &str?)

I know I can fix this by either using x.to_string(), or xs.into_iter() instead.

like image 243
viraptor Avatar asked Feb 09 '23 04:02

viraptor


1 Answers

Because ToOwned is implemented for T where T: Clone, and Clone is implemented for &T. You need to roughly understand how pattern matching on &self works when both T and &T are available. Using a pseudo-syntax for exposition,

str → String

  • str doesn't match &self
  • &str (auto-ref) matches &self with self == str

Thus ToOwned<str> kicks in.

&str → String

  • &str matches &self with self == str

Thus ToOwned<str> kicks in.

&&str → &str

  • &&str matches &self with self == &str

Thus ToOwned<&T> kicks in.

Note that in this case, auto-deref can never kick in, since &T will always match in cases where T might, which lowers the complexity a bit. Note also that auto-ref only kicks in once (and once more for each auto-deref'd type).

To copy from huon's much better answer than mine,

The core of the algorithm is:

  • For each each "dereference step" U (that is, set U = T and then U = *T, ...)
    1. if there's a method bar where the receiver type (the type of self in the method) matches U exactly , use it (a "by value method")
    2. otherwise, add one auto-ref (take & or &mut of the receiver), and, if some method's receiver matches &U, use it (an "autorefd method")

FWIW, .into() is normally prettier than .to_owned() (especially when types are implied; oft even when not), so I suggest that here. You still need a manual dereference, though.

like image 169
Veedrac Avatar answered Feb 19 '23 17:02

Veedrac