I'm having some trouble with references in rust. I have the following code that does not compile:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(&0, &0);
map.insert(&1, &1);
assert_eq!(map.get(&0), Some(&0));
}
The compilation error I get is:
error[E0308]: mismatched types
--> rust_doubt.rs:9:5
|
9 | assert_eq!(map.get(&0), Some(&0));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected &{integer}, found integral variable
|
= note: expected type `std::option::Option<&&{integer}>`
found type `std::option::Option<&{integer}>`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: aborting due to previous error
Sure enough, if I change the line:
assert_eq!(map.get(&0), Some(&0)); to assert_eq!(map.get(&0), Some(&&0)); (double ampersand) the code compiles
map.insert(&0, &0) inserts pointers to two integer literals into the map. I'm not sure how this is even possible since I have not used a variable anywhere. How can I have a reference to a literal? I was expecting the compiler to make me do this: let a = 0;
let b = 0
map.insert(&a, &b);
In other words, what does &0 even mean? Does it allocate memory for the literal and return a reference to it? If so, then am I correct in assuming that no two &0s would point to the same memory?
Some(&&0) instead of just Some(&0)? What does &&0 even mean? I understand that **ptr means dereferencing a variable twice to get the underlying value. But I can't quite imagine the reverse - how can you "refer" an integer literal twice?If you look at the signature of insert and get you will realize that they handle things differently.
Starting from a HashMap<K, V>:
fn insert(&mut self, k: K, v: V) -> Option<V>.fn get(&self, k: &K) -> Option<&V> (simplified).As you can see, insert takes ownership, handling values, while get takes and returns a reference.
Therefore, if you insert &1, you get Some(&&1) back: one more layer of reference.
The question, then, is why is there no error from .get(&0): isn't it lacking a level of reference?
Well, I cheated and simplified the signature of get, the exact signature is:
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V> where
K: Borrow<Q>,
Q: Hash + Eq,
And it turns out that &T implements Borrow<T>, so you can call get with &K for &&K.
If you manage to get the compiler to give you the type of the HashMap, it's a bit easier:
assert_eq!(map, ());
Results in:
error[E0308]: mismatched types
--> src/main.rs:9:5
|
9 | assert_eq!(map, ());
| ^^^^^^^^^^^^^^^^^^^^ expected struct `std::collections::HashMap`, found ()
|
= note: expected type `std::collections::HashMap<&{integer}, &{integer}>`
found type `()`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
Which shows you what type the compiler figured out for K and V, and indeed it will be &{integer}, since you pass &0 to insert which takes key and value by value.
As for the issue of lifetimes:
'static lifetime, just like "Hello" has the &'static str type.The compiler automatically reserves memory somewhere in the program for literals, and will "borrow" them as necessary. This means that creating a reference to a literal integer is perfectly fine: &0i32 has type &'static i32.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With