Reputation: 16361
Today's Rust mystery is from section 4.9 of The Rust Programming Language, First Edition. The example of references and borrowing has this example:
fn main() {
fn sum_vec(v: &Vec<i32>) -> i32 {
return v.iter().fold(0, |a, &b| a + b);
}
fn foo(v1: &Vec<i32>) -> i32 {
sum_vec(v1);
}
let v1 = vec![1, 2, 3];
let answer = foo(&v1);
println!("{}", answer);
}
That seems reasonable. It prints "6", which is what you'd expect if the
v
of sum_vec
is a C++ reference; it's just a name for a memory
location, the vector v1
we defined in main()
.
Then I replaced the body of sum_vec
with this:
fn sum_vec(v: &Vec<i32>) -> i32 {
return (*v).iter().fold(0, |a, &b| a + b);
}
It compiled and worked as expected. Okay, that's not… entirely crazy. The compiler is trying to make my life easier, I get that. Confusing, something that I have to memorize as a specific tic of the language, but not entirely crazy. Then I tried:
fn sum_vec(v: &Vec<i32>) -> i32 {
return (**v).iter().fold(0, |a, &b| a + b);
}
It still worked! What the hell?
fn sum_vec(v: &Vec<i32>) -> i32 {
return (***v).iter().fold(0, |a, &b| a + b);
}
type [i32] cannot be dereferenced
. Oh, thank god, something that makes sense. But I would have expected that almost two iterations earlier!
References in Rust aren't C++ "names for another place in memory," but what are they? They're not pointers either, and the rules about them seem to be either esoteric or highly ad-hoc. What is happening such that a reference, a pointer, and a pointer-to-a-pointer all work equally well here?
Upvotes: 10
Views: 1912
Reputation: 430671
The rules are not ad-hoc nor really esoteric. Inspect the type of v
and it's various dereferences:
fn sum_vec(v: &Vec<i32>) {
let () = v;
}
You'll get:
v
-> &std::vec::Vec<i32>
*v
-> std::vec::Vec<i32>
**v
-> [i32]
The first dereference you already understand. The second dereference is thanks to the Deref
trait. Vec<T>
dereferences to [T]
.
When performing method lookup, there's a straight-forward set of rules:
References in Rust aren't C++ "names for another place in memory,"
They absolutely are names for a place in memory. In fact, they compile down to the same C / C++ pointer you know.
Upvotes: 10