0

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
  1. The first print does not fail even when the value is moved. Is this because of primitive type or am I missing something?
  2. The second print seems ok.
  3. The third one prints a reference directly - shouldn't we get the memory address as this is a reference?
  4. The fourth print is a reference to a reference, which should print a memory address, I think?
  5. The fifth print seems reasonable as (I think) * 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

Community
  • 1
  • 1
treecoder
  • 43,129
  • 22
  • 67
  • 91

3 Answers3

2

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
2

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

tafia
  • 1,512
  • 9
  • 18
1

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

Community
  • 1
  • 1
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Can you please explain why it is `&**self` in that delegation, and why not `&*self` – treecoder May 26 '16 at 01:44
  • @treecoder the implementation is `for &'a T`, and there's a `&self` parameter (which is like saying `self: &Self`). Since `Self` is `&T`, the `self` parameter is a `&&T`, so it needs to be dereferenced twice. Maybe if I write `self` a few more times it will stop looking like a real word... – Shepmaster May 26 '16 at 01:53
  • Okay, this makes sense. Can you point me to some doc where I can know more about the differences between `self` and `Self`? – treecoder May 26 '16 at 05:33
  • 1
    @treecoder Self is a type shorthand referring to "the type in the current scope" (e.g. the T in `impl T`), self is just a reserved variable name that always refers to type `Self` (or &Self etc). – Linear May 27 '16 at 10:57