Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused by move semantics of struct fields inside a Box

Tags:

rust

If I do the following, I get an error:

struct A;
struct B;

fn consume_a(_a: A) {}
fn consume_b(_b: B) {}

struct C(A, B);

impl C {
    fn foo(self: Self) {
        consume_a(self.0);
        consume_b(self.1);
    }
}

fn main() {
    let c = Box::new(C(A, B));

    // Consume internals
    let _a = c.0;
    let _b = c.1;
}
error[E0382]: use of moved value: `c`
  --> src/main.rs:21:9
   |
20 |     let _a = c.0;
   |         -- value moved here
21 |     let _b = c.1;
   |         ^^ value used here after move
   |
   = note: move occurs because `c.0` has type `A`, which does not implement the `Copy` trait

I can achieve the same thing (consumption of internals) doing this:

fn main() {
    let c = Box::new(C(A, B));
    c.foo();
}

The way it works above (c.foo()) means that I have moved out of boxed content; how can this happen? None of the API's in Box's documentation show I can obtain the contained value as a type (i.e. all methods return &T or &mut T but not T)

like image 599
ustulation Avatar asked Jul 07 '16 10:07

ustulation


2 Answers

As you can see in the method, moving out of a struct's field directly works fine, but moving out of a field of a struct that's in a Box will first move out of the Box into a temporary variable and then move out of the field of that temporary. Thus when you try to move out of the second field, the Box has already been destroyed and there's just a temporary left that you can't use.

You can make this work by creating the temporary yourself:

let c2 = *c;
let _a = c2.0;
let _b = c2.1;
like image 184
oli_obk Avatar answered Sep 30 '22 11:09

oli_obk


Your original code works when non-lexical lifetimes are enabled:

#![feature(nll)]

struct A;
struct B;

fn consume_a(_a: A) {}
fn consume_b(_b: B) {}

struct C(A, B);

impl C {
    fn foo(self: Self) {
        consume_a(self.0);
        consume_b(self.1);
    }
}

fn main() {
    let c = Box::new(C(A, B));

    // Consume internals
    let _a = c.0;
    let _b = c.1;
}

This indicates that the original failure was simply a weakness of the borrow-checker.

like image 42
Shepmaster Avatar answered Sep 30 '22 12:09

Shepmaster