简体   繁体   中英

How does Box<dyn Trait> deconstruct itself?

Since it doesn't know the concrete type of the data, it only contains a vtpr of dyn Trait, How does it drop itself when it goes out of scope? Does every virtual table in Rust contains a drop method implementation?

When the concrete type the original Box contained is unsized into a trait object, the Drop implementation for the type goes into the vtable. A pointer (Any pointer-like thing in Rust. IE, a reference, Box , raw pointer, etc.) whose pointee is a trait object is laid out as follows in memory*:

struct FooTraitDynPointer {
    ptr: *[const/mut] (),
    vtable: &'static VTableImplForFooTrait
}

The ptr field in my example points to the actual data. We could say that's the original Box .

The vtable field in my example points to a static vtable. Say we have the following Foo trait:

trait Foo {
    fn bar(&self) -> usize;
}

Our vtable will look as follows*:

struct VTableImplForFooTrait {
    dropper: unsafe fn(*mut ()),
    size: usize,
    align: usize,
    bar: unsafe fn(*const ()) -> usize,
}

We see there, that the drop is there. Along with it, there're size and align fields which allow owning types to deallocate enough memory. Or re-allocate enough memory.

Here's an example program which crudely extracts the size of a struct from within a pointer to a trait object:

#![feature(raw)]

trait Foo {
    fn bar(&self) -> usize;
}

struct Baz {
    field: f64
}

impl Foo for Baz {
    fn bar(&self) -> usize {
        self.field as usize
    }
}

#[derive(Clone)]
struct FooVTable {
    dropper: unsafe fn(*mut ()),
    size: usize,
    align: usize,
    bar: unsafe fn(*const ()) -> usize,
}

fn main() {
    use std::{mem, raw};
    let value = Baz { field: 20.0 };

    let boxed = Box::new(value) as Box<dyn Foo>;

    let deconstructed: raw::TraitObject = unsafe { mem::transmute(boxed) };

    let vtable = deconstructed.vtable as *mut FooVTable;

    let vtable = unsafe { (*vtable).clone() };

    println!("size: {}, align: {}", vtable.size, vtable.align);

    let result = unsafe { (vtable.bar)(deconstructed.data) };

    println!("Value: {}", result);
}

Playground

(Currently) prints:

size: 8, align: 8
Value: 20

However this may very well change in the future so I'm leaving this timestamp here for someone who reads this in a future where the behaviour has been changed. June 5, 2020.


*: The layout of trait objects, and especially their vtables is NOT guaranteed, so do not rely in actual code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM