I have problems understanding the ref
pattern in Rust. I am referring to https://rustbyexample.com/scope/borrow/ref.html
Here is the code I don't understand:
let point = Point { x: 0, y: 0 };
let _copy_of_x = {
// `ref_to_x` is a reference to the `x` field of `point`
let Point { x: ref ref_to_x, y: _ } = point;
// Return a copy of the `x` field of `point`
*ref_to_x
};
I get that the last let
expression(?) is some sort of pattern matching. So it's my understanding ref ref_to_x
should be equal to 0
, the x
value of the original point
.
But I don't understand what the ref
actually does. When I add some code like this:
println!("x: {}", point.x);
println!("ref_to_x: {}", ref_to_x);
println!("*ref_to_x: {}", *ref_to_x);
I always get 0
, so there doesn't seem to be a difference. Somehow I'd expect a memory address for ref_to_x
while *ref_to_x
might be the dereferenced value again.
I can replace both ref ref_to_x
and *ref_to_x
with myx
and the code still works. What's the difference? What does ref
do exactly?
edit: after reading dbaupps answer and doing some addition with ref_to_x
and *ref_to_x
things got a bit clearer; you cannot add an integer to ref_to_x
because it's a reference. I guess I got confused because there is no indication of a reference when you print one.
A reference created with
ref
is exactly the same as reference taken with&
.The difference is where they're allowed in the syntax.
ref
on the left side of an assignment is like adding&
on the right side.These expressions are equivalent:
This redundancy exists because in pattern matching
&
is used to require that a reference exists already, rather than to make a new one:(discussion)
ref
creates a pointer into the piece of memory that is being matched on, in this case,ref_to_x
is pointing directly to the memory that storespoint.x
, it is the same as writinglet ref_to_x = &point.x
in this case.The pattern is extremely important, as it allows one to reach deep inside complicated data-structures without disturbing the ownership hierarchy. For example, if one has
val: &Option<String>
, writingis not legal, it gives an error like:
It is not legal to take ownership (move) out of a borrowed value, because that would possibly damage the thing from which the value was borrowed (violating its invariants, causing data to disappear unexpectedly, etc.).
So, one can instead use a reference to just point in the memory with a borrowing
&
reference.There's a slight subtlety here because (a)
point
isn't borrowed, so it is OK to move out ofpoint
(which consumes ownership ofpoint
too, meaning it can't be used later unless reinitialised), and (b) the typeint
isCopy
, so doesn't move ownership when used by value. This is why usingmyx
instead works fine. If the type ofx
was, say,String
(which isn'tCopy
) andpoint
was borrowed, then theref
will be necessary.