Deref with Box<T>

The following code works:

fn deref_example_1() {
    let x = Box::new(String::from("Rust"));

    let y = *x;
    println!("{y}");
}

In the Rust Book it says:

When we entered *y in Listing 15-9, behind the scenes Rust actually ran this code:

*(y.deref())

So I would assume the following to work as well

fn deref_example_2() {
    let x = Box::new(String::from("Rust"));

    // error[E0507]: cannot move out of a shared reference
    let y = *(x.deref());
    //      ^^^^^^^^^^^^ move occurs because value has type `String`,
    //                   which does not implement the `Copy` trait
    println!("{y}");
}

But it fails, as shown in the comments.

Why is this the case?

2 Likes

Box is special in a few ways, including the ability to move values out via dereference.

1 Like

But if the contents of the box were a number, like in the code that the book used before that sentence, then *(box.deref()) would succeed because the contents of the reference can be copied out.

2 Likes

Ok, trying to summarize:

*x

  • Does dereference and creates place where the value can be moved from, in a single step, due to special handling of Box<T> in the language
  • Assigning to y does a move

*(x.deref())

  • Looses the ability to move, because there is no special compiler handling anymore
  • Assigning to y requires to do a move, because there is no Copy instance for String
  • Move is not possible due to: error[E0507]: cannot move out of a shared reference

Is this correct?

Yes.

If we’re being pedantic, there still is special compiler handling involved, but not for dereferncing of Box. As you can see, *y desugars to another expression containing *-operator, so this desugaring can’t go on forever in any case. The way this works is that besides Box<T>, the reference types &T and &mut T also use a built-in implementation of the dereferencing operator. Unlike Box, those two types don’t support moving out of the dereferenced place though.[1] *(x.deref()) features the dereferencing operator “*” applied to the value x.deref() which desugars to <Box<String> as Deref>::deref(&x). The function <Box<String> as Deref>::deref has signature fn(&Box<String>) -> &String, so the dereferencing operator operates on a value of type &String.

This is also why it claims “cannot move out of a shared reference”, as that’s exactly what it’s trying to do.


  1. And this explains the error, and furthermore the precise error message. ↩︎

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.