Search code examples
rusttraitsdereference

why does dereferencing a Box<T> not complain about "moving out of shared reference", when T is not a Copy?


As the title states, why does the following code compile?

use std::ops::Deref;

struct Foo {}

fn main() {
    let b = Box::new(Foo{});
    
    let pb = *b; //works
    // let pb = *b.deref();  // error[E0507]: cannot move out of a shared reference
}

The same does not hold true for a custom type with non-Copy target.

use std::ops::Deref;

struct Foo<T> {
    p: T,
}

impl<T> Deref for Foo<T> {
    type Target = T;

    fn deref(&self) -> &<Self as Deref>::Target {
        &self.p
    }
}

fn main() {
    let f = Foo { p: vec![1u8] };

    let pf = *f; // error[E0507]: cannot move out of a shared reference
    let pf = *f.deref(); // error[E0507]: cannot move out of a shared reference
}

Is *Box not doing *Box.deref()?


Solution

  • Is *Box not doing *Box.deref()?

    No, it is not. This is one of those cases of compiler magic -- the Box type is a bit special and the compiler treats it specially.

    Looking at the Deref implementation for Box gives us a very strong hint that something is different:

    fn deref(&self) -> &T {
        &**self
    }
    

    If this were any other type, this would cause infinite recursion, but the deref operator (*) is handled internally be the compiler when applied to a Box value.

    One of the special things you're allowed to do with boxes is move its contained value out, which has the side-effect of destroying the box and freeing its heap allocation.

    (Also, the unstable Box::into_inner() associated function takes boxed: Self and simply returns *boxed, further illustrating this.)