Is double dereference Box<T> to get `&T` the compiler magic?

#[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   // multiple times of the dereference
    }
}
    let d = Box::new(10);
    let c = d.deref();  // get a reference to i32

As a contrast

struct Wrapper(i32);

impl Deref for Wrapper{
    type Target = i32;

    fn deref(&self) -> &Self::Target {  // Compiler warns that "recursive call site"
        &**self
    }
}

Is &**self in the implementation of Deref for Box the compiler magic?

Not sure why Google-foo failed you because for me search for why rust box is special sends me to both previous thread on the topic and the appropriate blog post.

2 Likes

It is not. This deref isn't even used. Box is a built-in, and you can't make a Box-like type in user code, unfortunately.

1 Like

These impls are actually used when referencing them through generics. For example

use core::ops::Deref;

fn foo(a: &impl Deref<Target = u32>) -> &u32 {
    &**a // don't know the concrete type of a, so has to refer to Deref::deref
}

pub fn bar() {
    foo(&Box::new(0));
}
; <alloc::boxed::Box<T,A> as core::ops::deref::Deref>::deref
; Function Attrs: nonlazybind uwtable
define align 4 i32* @"_ZN74_$LT$alloc..boxed..Box$LT$T$C$A$GT$$u20$as$u20$core..ops..deref..Deref$GT$5deref17h94e506c0f5dd2922E"(i32** align 8 %self) unnamed_addr #1 !dbg !763 {
start:
  %self.dbg.spill = alloca i32**, align 8
  store i32** %self, i32*** %self.dbg.spill, align 8
  call void @llvm.dbg.declare(metadata i32*** %self.dbg.spill, metadata !771, metadata !DIExpression()), !dbg !772
  %_2 = load i32*, i32** %self, align 8, !dbg !773, !nonnull !98, !align !774, !noundef !98
  ret i32* %_2, !dbg !775
}

; playground::foo
; Function Attrs: nonlazybind uwtable
define internal align 4 i32* @_ZN10playground3foo17h7fae4ae4a9075a9fE(i32** align 8 %a) unnamed_addr #1 !dbg !816 {
start:
  %a.dbg.spill = alloca i32**, align 8
  store i32** %a, i32*** %a.dbg.spill, align 8
  call void @llvm.dbg.declare(metadata i32*** %a.dbg.spill, metadata !820, metadata !DIExpression()), !dbg !823
; call <alloc::boxed::Box<T,A> as core::ops::deref::Deref>::deref
  %_2 = call align 4 i32* @"_ZN74_$LT$alloc..boxed..Box$LT$T$C$A$GT$$u20$as$u20$core..ops..deref..Deref$GT$5deref17h94e506c0f5dd2922E"(i32** align 8 %a), !dbg !824
  br label %bb1, !dbg !824

bb1:                                              ; preds = %start
  ret i32* %_2, !dbg !825
}

What basically happens is that if the type is know to be a Box, it replaces the deref with a compiler builtin deref operation and if not inserts a real Deref::deref call. Inside the Deref impl for Box, it knows that it is a Box and as such does replace it with a builtin deref operation. The same happens for other operations with builtin impls, like math on integers and floats.

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.