Reputation: 45081
I have the following simple program
fn main() {
let a = 10;
let b: i32;
let r: &i32;
b = a; // move?
r = &a; // borrow?
println!("{}", a);
println!("{}", b);
println!("{}", r);
println!("{}", &r);
println!("{}", *r);
}
The output is
10
10
10
10
10
*
is the value at
operator that de-references the reference.It seems I am not quite getting the whole thing.
Please explain in detail what's going on.
Related: Move vs Copy in Rust
Upvotes: 0
Views: 124
Reputation: 430554
The other answers are mostly right, but have some small errors.
1. i32
implements Copy
, so when you assign it to a second variable binding, the first binding does not need to be invalidated. Any type that implements Copy
will have this property.
3. You have asked to format the value with {}
which corresponds to the Display
trait. There is an implementation of this trait for references to types that implement Display
:
impl<'a, T> Display for &'a T where T: Display + ?Sized {
fn fmt(&self, f: &mut Formatter) -> Result { Display::fmt(&**self, f) }
}
This simply delegates to the implementation of the referred-to type.
4. The same as #3 - a reference to a reference to a type that implements Display
will just delegate twice. Deref
does not come into play.
Here's the sneaky thing that no one else has mentioned. println!
is a macro, which means it has more power than a regular function call. One of the things that it does is automatically take a reference to any arguments. That's what allows you to print out a value that doesn't implement Copy
without losing ownership.
With this code:
let a = 10;
println!("{}", a);
The expanded version is actually something like this (slightly cleaned up):
let a = 10;
static __STATIC_FMTSTR: &'static [&'static str] = &["", "\n"];
::std::io::_print(::std::fmt::Arguments::new_v1(__STATIC_FMTSTR, &match (&a,) {
(__arg0,) => [::std::fmt::ArgumentV1::new(__arg0, ::std::fmt::Display::fmt)],
}));
Therefore, everything passed to println!
is a reference. It wouldn't be very useful if references printed out memory addresses.
Besides the usefulness, Rust focuses more on value semantics as opposed to reference semantics. When you have values moving and changing addresses frequently, the location of the value isn't very consistent or useful.
See also
Upvotes: 1
Reputation: 88556
Regarding 1: Yes, because it's a primitive variable, more specifically a type that implements the Copy
trait. All those Copy
-types work with copy semantics instead of move semantics.
Regarding 3: println!
automatically dereferences it's arguments -- this is what the user wants in 99% of all cases.
Regarding 4: Again, automatically dereferences arguments... until it's a non-reference type.
Upvotes: 2
Reputation: 1562
1, 2 => You are working with i32
, which is Copy
, so in practice b = a.clone()
3, 4, 5 => You're confused with the Deref
trait. I find it easier to reason about ownership/borrowing than references in rust. r = &a
means r
borrows a
so I can access its value later on, someone else will own it and take care of dropping it
Upvotes: 2