Why adding/removing referencing does not change how code works?

I'm playing around with the chapter on HashMaps in "Rust by Example". I was wondering why in the following code at the end of this chapter:

    for (contact, &number) in contacts.iter() {
        println!("Calling {}: {}", contact, call(number)); 
    }

somehow number is referenced with an & while contact is not, and why are they handled differently. So I've started tweaking this code, and all four possible permutations print exactly the same output:

contact, number
contact, &number
&contact, number
&contact, &number
(snip)
Calling Daniel: Hi! Who is this again?
Calling Robert: Hi! Who is this again?
Calling Katie: Hi! Who is this again?

Can someone explain why adding/removing an ampersand does not change code's meaning? TIA.

Well, what do you expect to happen?


By the way, it's because of the transitive (blanket) impl Display for &T where T: Display: Display in std::fmt - Rust

1 Like

First let's make sure we understand what types we're dealing with:

    let _: () = contacts.iter().next();

40 |     let _: () = contacts.iter().next();
   |            --   ^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found enum `Option`
   |            |
   |            expected due to this
   |
   = note: expected unit type `()`
                   found enum `Option<(&&str, &&str)>`

So here, we're binding against a (&&str, &&str):

    for (contact, &number) in contacts.iter() {
    // similar to
    // let (contact, &number) = (&"some literal str", &"another literal");

And in case you're not familiar with how non-trivial bindings work, adding the & on the left destructures or unwraps a layer of reference when bound to the value on the right. Note how in the below diagram, contact's binding includes the & of the value, while number's binding excludes it (as it's been "absorbed" by the & you added on the left).

let  
(             contact                , &              number              )= 
//  ._________|     |___________.           ._________|    |__________.
(/* | */ &"some literal str" /* | */ , & /* | */ "another literal" /* | */);

So contact is a &&str and number is a &str. In all your variations, the types of the bindings are some combination of &str and &&str. With this understanding, the question becomes: Why do &str and &&str display the same?

And as was already answered, Rust tends to "see through" indirection like this, at least for things like Eq or Display. Thus they ultimately do the same thing when you print them.

4 Likes

Thanks a lot, this was really useful.