Box <dyn Trait>如何自我解构?

时间:2020-06-05 03:53:08

标签: rust

由于它不知道数据的具体类型,因此仅包含dyn Trait的vtpr,如果超出范围,它将如何丢弃? Rust中的每个虚拟表都包含drop方法的实现吗?

1 个答案:

答案 0 :(得分:5)

当包含的原始类型r1 + r2的具体类型未调整为特征对象的大小时,该类型的Box实现将进入vtable。指针(其指针是特征对象)的指针(Rust。IE中的任何类似指针的东西,引用,Drop,原始指针等)在内存中的布局如下:

Box

在我的示例中,struct FooTraitDynPointer { ptr: *[const/mut] (), vtable: &'static VTableImplForFooTrait } 字段指向实际数据。我们可以说这是原始的ptr

在我的示例中,Box字段指向静态vtable。假设我们具有以下vtable特征:

Foo

我们的vtable如下*:

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

我们在这里看到struct VTableImplForFooTrait { dropper: unsafe fn(*mut ()), size: usize, align: usize, bar: unsafe fn(*const ()) -> usize, } 。伴随着它,还有size和align字段,这些字段允许拥有的类型释放足够的内存。或重新分配足够的内存。

这是一个示例程序,该程序从指向特征对象的指针中粗略提取结构的大小:

drop

Playground

(当前)打印:

#![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);
}

但是,将来这可能会发生很大的变化,因此我将这个时间戳留在这里,供将来在行为已更改的情况下阅读此时间戳的人使用。 2020年6月5日。


*:不能保证特征对象的布局,尤其是其vtable的布局,因此不要依赖实际的代码。