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?
A pointer to reference is illegal in C++, because -unlike a pointer- a reference is just a concept that allows the programmer to make aliases of something else. A pointer is a place in memory that has the address of something else, but a reference is NOT.
A pointer is a general concept for a variable that contains an address in memory. This address refers to, or “points at,” some other data. The most common kind of pointer in Rust is a reference, which you learned about in Chapter 4. References are indicated by the & symbol and borrow the value they point to.
Once a reference is established to a variable, you cannot change the reference to reference another variable. To get the value pointed to by a pointer, you need to use the dereferencing operator * (e.g., if pNumber is a int pointer, *pNumber returns the value pointed to by pNumber .
References are used to refer an existing variable in another name whereas pointers are used to store address of variable. References cannot have a null value assigned but pointer can. A reference variable can be referenced by pass by value whereas a pointer can be referenced by pass by reference.
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.
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