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 &0
s 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