Dereferencing a Boxed value

How does dereferencing of a Box work? When one dereferences a boxed value one gets the internal value not a reference. Any idea what is happening behind the scenes? Thanks.

1 Like

Dereferencing a pointer-like value with *foo syntax always logically gives you an expression denoting the value (more precisely the “place” of the value) behind the indirection; you only get a reference (mutable or immutable) if you take a reference to this expression, e.g. &*foo or &mut *foo, or anything that implicitly works by-reference.

This works the same for Box, too. The difference is that accessing this expression *foo by value only works for T: Copy target types in case of foo: &T / foo: &mut T / foo: Arc<T> etc… whereas Box<T> also allows such an access for non-Copy types T, in which case, the value is moved out of the box.

This ability of Box is still a “magical” ability, not reproducable for any other custom type, since it doesn’t function by means of implementing any trait such as Deref/DerefMut; mainly because capturing this interface is kinda hard.

A natural misconception is that for foo: Box<T>, doing *foo (and moving out of the resulting place) would simply be sugar for calling a fn(Box<T>) -> T API, consuming the box.[1] This is not the case. The Box<T> value is still there until the end of its scope; it doesn’t contain a value anymore, but the memory still exists, and you can fill it up again. E.g. see this code

fn replace_box_contents<T>(x: Box<T>, y: T) -> Box<T> {
    let mut the_box = x;

    // moves old value out
    let old_value = *the_box;

    // could not access `the_box` here without compilation error
    // e.g. `let _reference = &the_box;` would fail here with
    // “borrow of moved value: `the_box`”

    // move new value back in
    *the_box = y;

    // can access again, etc…
    let _reference = &the_box;

    // return it
    the_box
}

this ability of Box<T> works comparably to the ability to move out of (and back into) fields of structs (that don’t implement Drop):

struct MyStruct<T>(T);

fn replace_struct_contents<T>(x: MyStruct<T>, y: T) -> MyStruct<T> {
    let mut the_struct = x;

    // moves old value out
    let old_value = the_struct.0;

    // could not access `the_struct` here without compilation error
    // e.g. `let _reference = &the_struct;` would fail here with
    // “borrow of partially value: `the_struct`”

    // move new value back in
    the_struct.0 = y;

    // can access again, etc…
    let _reference = &the_struct;

    // return it
    the_struct
}

I prefer that the error speaks of ”partially moved“ in this case; makes it more easy to learn that the_struct still exists after moving the_struct.0, whereas the Box example above makes it harder to learn that the_box still contains some Box value and merely its target has been moved. By the way, this also works behind multiple levels of indirection or structs, etc…

struct MyStruct<T>(T);

fn replace_boxed_struct_contents<T>(x: Box<MyStruct<T>>, y: T) -> Box<MyStruct<T>> {
    let mut the_boxed_struct = x;

    // moves old value out
    let old_value = the_boxed_struct.0;

    // could not access `the_boxed_struct` here without compilation error
    // e.g. `let _reference = &the_boxed_struct;` would fail here with
    // “borrow of partially value: `the_boxed_struct`”

    // move new value back in
    the_boxed_struct.0 = y;

    // can access again, etc…
    let _reference = &the_boxed_struct;

    // return it
    the_boxed_struct
}

(Of course this function is just a toy example; for actually replacing a value like this when you have the replacement readily available already, you could just assign to *the_box directly to replace and drop the old value, or use
std::mem::replace replace and keep the old value, but (either way) making it work by just mutable-reference access to the place being replaced.)


  1. And if this were the case, the support for this operation would be trivially easy to express with a trait. But it isn’t, hence we still have no “DerefOwned”-version of Deref/DerefMut that could make what Box<T> offers less magical. ↩︎

7 Likes

The ability to move out of a deref'd box is sometimes called DerefMove (though no such trait actually exists yet). Some hope it will be less magical in the future.

2 Likes

Ah, DerefMove it was? I called it DerefOwned in the footnote I had added to my post above.

That's the terminology I'm familiar with, yeah (there's an old RFC somewhere).

(Missed the footnote, sorry - mobile)

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.