2

this is error:

struct A(i32);

fn main(){
    let a = A(1);
    let ra = &a;
    let x = *ra;
}

but this is ok(only change & to Box::new):

struct A(i32);

fn main(){
    let a = A(1);
    let ra = Box::new(a);
    let x = *ra;
    // (1)
}

what is happenning here?

when I add this at (1):

let ra2 = ra;

it say "use of moved value: ra". But I just move the *ra. if in seconed code it moves ra, then in first code why it doesn't move ra?

I write my box(via The Rust Programming Language 15.2), but it also cannot moves ra, and report an error:

use std::ops::Deref;
struct A(i32);

struct MyBox<T>(T);

impl<T> MyBox<T> {
    fn new(x: T) -> MyBox<T> {
        MyBox(x)
    }
}
impl<T> Deref for MyBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

fn main(){
    let a = A(1);
    let ra = MyBox::new(a);
    let x = *ra;
}
yymmyb
  • 84
  • 6
  • There are borrow-checker errors in both of the code snippets after adding the `ra2` line. What makes you think one of them works? – Silvio Mayolo Apr 07 '21 at 02:23
  • 1
    `Box` is extra special. See [How do I get an owned value out of a `Box`?](/q/42264041/3650362) and [Dereferencing Box gives back value instead of reference](/q/33653946/3650362) – trent Apr 07 '21 at 02:30
  • 1
    Elaborating on @trentcl's comment: `Box` is considered a built-in reference/pointer type, equally as much as `&T` and `&mut T`. That rarely matters, but this is one of those rare cases. The dereference operator creates a "place expression" for built-in references/pointers, but for everything else (including "smart pointers" like `MyBox`), it acts as `*Deref::deref(&ra)`, so in this case `*&ra.0`, hence the error. If it seems like that doesn't make sense, it's because it *doesn't* really make sense. It's just a quirk of Rust. – Coder-256 Apr 07 '21 at 03:03
  • @Coder-256 `*Deref::deref(&ra)` is still a place expression, just one that can't be moved out of. `&T` and `&mut T` are special, too, in that they don't have to go through `Deref` and `DerefMut`, but the fact that `Box` can be destructively moved out of with `*` is mostly orthogonal to that, and what makes it *extra* special. /nitpick – trent Apr 07 '21 at 13:43

2 Answers2

1

In your first example what happened:

struct A(i32);

fn main(){
    let a = A(1); // A(1) is in `a` variable
    let ra = &a; // `ra` references to `a`. 
    println!("{}", a.0); // `a` itself is still exists here and you can read it
    let x = *ra; // A(1) is being tried to be moved from `a`. Error here
    let x = a; // but you can move value from `a` directly. It's ok here.
    // `a` does not exist any more
    // println!("{}", a.0); // error here 
}

In the line let x = *ra; you are trying to "extract" value from behind the reference. But ra does not own the value. It owned by a. So you can't move value from variable, if this variable does not own it.

In your second example different thing happened.

struct A(i32);

fn main(){
    let a = A(1); // A(1) is in `a`
    let ra = Box::new(a); // value `A(1)` is moved from `a` to boxed.
    // you can't access to `a` now:
    // println!("{}", a.0); // error here
    let x = *ra; // here you are trying to move value from the memory location where `Box` points to.
    // `ra` does not exist any more!
    // println!("{}", ra.0); // error here. Used of moved value
}

Box owns A(1). As long as there is no references to A(1) you can move value behind the Box.

If you will try to get reference to the value behind the Box, then you also can't move by dereferencing:

struct A(i32);

fn main(){
    let a = A(1); // A(1) is in `a`
    let ra = Box::new(a); // value `A(1)` is moved from `a` to box.
    let ra_ref = &ra;
    // both `ra_ref` and `ra` are accesseble here
    
    // let x = *ra; // here you are trying to move value from the memory location where `Box` points to. Error as long as there `ra_ref`
    println!("{}", ra_ref.0);
}
Dmitry
  • 1,426
  • 5
  • 11
1

According to Moved and Copyed Types

When a place expression is evaluated in a value expression context, or is bound by value in a pattern, it denotes the value held in that memory location. If the type of that value implements Copy, then the value will be copied. In the remaining situations, if that type is Sized, then it may be possible to move the value. Only the following place expressions may be moved out of:

  • Variables which are not currently borrowed.
  • Temporary values.
  • Fields of a place expression which can be moved out of and don't implement Drop.
  • The result of dereferencing an expression with type Box and that can also be moved out of.
struct A(i32);

fn main(){
    let a = A(1); // A(1) is in `a`
    let ra = Box::new(a); // value `A(1)` is moved from `a` to box.

    // `ra` is a place expression(`ra` is a variable), 
    // and it is evaluated in a value expression context. According to that 
    // rule, if `ra` is a Box, then the move semantic happens. so this is ok.

    let b = ra; 
    }

But for this code

struct A(i32);

fn main() {
    let a = A(1);
    let ra = &a; // ra is a borrow of a
    let b = *ra; // `*ra` is also a place expression according to
    // the definition of `place expr and value expr`
    // and *ra is in a value expression context, so it is referencing
    // the value of `ra`, i.e. `a` with type `A`, `A` does not impl Copy
    // so it may be moved, but it does not satisfy the above four moving
    // rules. So, an error will occur.
}

place expr and value expr

pgplus1628
  • 1,294
  • 1
  • 16
  • 22