How did a reference of a reference become value?

Considering the below code snippet. elem is type &String

let list = vec![String::from("Alice"), String::from("Bob"), String::from("Catherine")];
    for elem in list.iter() {
        println!("{}", elem == true) // Type of elem is &String
    }

Over here I would expect type of elem to become &(&String) but it became String ??? Can someone help to explain how did a reference of a reference become the value instead???

let list = vec![String::from("Alice"), String::from("Bob"), String::from("Catherine")];
    for &elem in list.iter() {
        println!("{}", elem == true) // Type of elem is String
    }

Edit: Also look at my next answer below, I think I did misunderstand you while writing this one.




You mean it became &String, right?

Why would you expect &&String here though? You are calling list.iter(). The method iter when called on a Vec<T> is available due to “dereferencing” to the slice type, [T], and takes a &self argument, so it’s called on &[T]. Your list is a Vec<String>, so you’re calling iter on a &[String]. Looking at it’s documentation it returns a slice::Iter<'_, T>, and if you hover over that “ⓘ” symbol, you see that that type is an iterator with “type Item = &'_ T;” in other words: Since T is String, the iterator contains items of type &String. That’s the type of elem.

Also, intuitively speaking, an iter() method is usually supposed to return a by-shared-reference iterator, i.e. an iterator that contains shared references to the elements of a collection. For a Vec<String> you’d expect items of type &String, which is exactly the case here, as I explained above.

The second code example where you write for &elem in list.iter() uses a reference pattern, by writing something like that, you’ll be trying to dereference the item of type &String to a String. This specific kind of dereferencing is however only possible for simple types such as i32 or bool etc (or more generally everything that implements Copy, the second code example won’t work for strings. If you e.g. comment out the part that triggers the type error by trying to compare String to bool with ==, then you’ll get an error like

error[E0507]: cannot move out of a shared reference
 --> src/main.rs:3:18
  |
3 |     for &elem in list.iter() {
  |         -----    ^^^^^^^^^^^
  |         ||
  |         |data moved here
  |         |move occurs because `elem` has type `String`, which does not implement the `Copy` trait
  |         help: consider removing the `&`: `elem`

Oh, I think I misread your post. You’re asking why writing for &elem in ... instead of for elem in ... turns &String into String instead of &&String. As I mentioned above that’s due to how & works in patterns.

This is analogous to something like

struct Foo<T>(T);

let x = Foo(42);
// x is `Foo<i32>`

let Foo(y) = x;
// y is `i32` and not `Foo<Foo<i32>>

Patterns usually do kind-of “the opposite” of expressions, so we have

let x = &42;
// x is `&i32`

let &y = x;
// y is `i32` and not `&&i32`

Also, as mentioned above, this doesn’t actually work for String like that. The assignment let &y = x; copies the value 42 into y.

for loops are similar to let expressions:

struct Foo<T>(T);

let x = vec![Foo(42), Foo(69)];
// x is Vec<Foo<42>>`

for y in x.iter() {
    // y is &Foo<42>
}

for &y in x.iter() {
    // y is Foo<42>
}

for &Foo(y) in x.iter() {
    // y is 42
}

There’s also more to pattern than this, in particular you can re-introduce the indirection with something like ref, as in

struct Foo<T>(T);

let x = vec![Foo(42), Foo(69)];
// x is Vec<Foo<42>>`

for &Foo(ref y) in x.iter() {
    // y is &42
}

This has the benefit that it avoids the need to copy the type and it also works with something like String again:

struct Foo<T>(T);

let x = vec![Foo("hello".to_string()), Foo("world".to_string())];
// x is Vec<Foo<String>>`

for &Foo(ref y) in x.iter() {
    // y is &String
}

/* this wouldn’t work:
for &Foo(y) in x.iter() {
    // y would have to be String
}
*/

Patterns like &Foo(ref x) had become so common in earlier Rust versions that there’s a shorter syntax for them

struct Foo<T>(T);

let x = vec![Foo("hello".to_string()), Foo("world".to_string())];
// x is Vec<Foo<String>>`

for &Foo(ref y) in x.iter() {
    // y is &String
}
// the above is equivalent to:
for Foo(y) in x.iter() {
    // y is &String
}

To clarify, the ref in the above examples does exactly what you seem to have expected & do to, in particular if you try

let list = vec![String::from("Alice"), String::from("Bob"), String::from("Catherine")];
for ref elem in list.iter() {
    println!("{}", elem == true) // Type of elem is String
}

you’ll see that now elem is &&String. Of course introducing an extra reference level in this situation is relatively useless, you’ll probably never see anyone write something like for ref elem in ... ever.

1 Like

Thanks for the detailed explanation. It really helped me to understand what's going on. Didn't know that pattern matching is going on for for in construct