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:
let ref x1 = y;
let x2 = &y;
This redundancy exists because in pattern matching &
is used to require that a reference exists already, rather than to make a new one:
let foo = 1;
match foo {
ref x => {
/* x == &1 */
match x {
&y => /* y == 1 */
}
},
}
(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 stores point.x
, it is the same as writing let 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>
, writing
match *val {
Some(s) => println!("the string is {}", s),
None => println!("no string"
}
is not legal, it gives an error like:
<anon>:3:11: 3:15 error: cannot move out of borrowed content
<anon>:3 match *val {
^~~~
<anon>:4:14: 4:15 note: attempting to move value to here
<anon>:4 Some(s) => {}
^
<anon>:4:14: 4:15 help: to prevent the move, use `ref s` or `ref mut s` to capture value by reference
<anon>:4 Some(s) => {}
^
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 of point
(which consumes ownership of point
too, meaning it can't be used later unless reinitialised), and (b) the type int
is Copy
, so doesn't move ownership when used by value. This is why using myx
instead works fine. If the type of x
was, say, String
(which isn't Copy
) and point
was borrowed, then the ref
will be necessary.
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