How understand &**self in the deref trait in Box<T>

let b = Box::new(String::from("1"));
b.to_string();

Here, because the type is wrong, the compiler automatically tries to dereference b by calling b.eref ();
But looking at the &**self in the source code of Box, I really don't understand how it returns a T type.

#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_box", issue = "92521")]
impl<T: ?Sized, A: Allocator> const Deref for Box<T, A> {
    type Target = T;

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

1, self is an &Box type, *self gets a Box type.
2, **self=>*Box, dereference again, should still call deref? But doesn't that make it infinitely recursive? At least my own smart pointer, written like this, will make infinite recursive errors.
So the second step is not clear here. I hope the big guys can answer my questions, thank you!

The * operator for Box is actually a buitin operation and doesn't go through the Deref trait itself.

The situation is similar for things like impl Add for i32 which (of you manage to find the source code in the middle of the relevant macros) itself is simply defined using the + operator.


The Box::deref method would only be called in generic contexts, e. g. if you had a fn f<T: Deref>(x: T) function that uses the *x operation (or implicit dereferencing) and then call that function f with a Box.

In the case of dereferencing a Box, this operation being built in also allows it to have two somewhat magical superpowers.

On one hand, Box<T> allows you to move out it's value via dereferencing, so you can do

fn demo(x: Box<String>) -> String {
    *x
}

and you can also replace it's contents without using mem::replace (which is not super useful per say, but it works) e. g.

fn demo2(x: Box<String>, other: String) -> (String, Box<String>) {
    let mut bx = x;
    let old = *bx;
    *bx = other;
    (old, bx)
}

The other thing it allows is a more fine-grained understanding from the borrow+checker, so you can write something like

fn demo3(x: &mut Box<(i32, u8)>) -> (&mut i32, &mut u8) {
    (&mut x.0, &mut x.1)
}

whereas for typical types with Deref operator, you'd need to create a single &mut (i32, u8) reference first, then split the borrow from that.

3 Likes

Thank you very much!!
I think I understand a little bit. Can I understand it as C++ overloading the * operator? It's just that in RUST, this is implemented by the compiler, and we don't see the associated source code?

That's not a bad analogy to start with, but I'd suggest avoiding analogies and accepting that the definition of the * operator just is that

  • if the type is any of the built-in pointer types (which Box is really one of by way of being a lang item), it follows the pointer;
  • else if the type implements Deref or DerefMut as needed for the context, call that;
  • else report ā€œcannot be dereferencedā€.

The Rust Reference says more or less this.

4 Likes

Thank you. Your advice was very helpful!

recommendation to read this blog post: Box is special

1 Like

Surprised! I can't believe there's such a good blog. Thank you very much! Let people know that there are such good bloggers.