试图理解拉斯特的vables是如何工作的,但失败了.

struct Struct {
    data: u32
}

impl Struct {
    fn my_data(&self) -> u32 { self.data }
}

trait Trait {
    fn get_data(&self) -> u32;
}

impl Trait for Struct {
    fn get_data(&self) -> u32 { self.my_data() }
}

fn what_data(a: &dyn Trait) -> u32 {
    // let ptr = &a.get_data as *const _; // I actually need to get a raw pointer to a.get_data here
    a.get_data()
}

fn main() {
    let a = Struct { data: 42 };
    println!("{}", what_data(&a));
}

如果指向声明函数的特征的指针为&dyn Trait,如何获取指向函数入口点的原始指针?

推荐答案

Rust的vtable struct 不能从Rust代码访问.

如果你只是想了解这是如何工作的,那么我们可以使用一些不安全的魔法(but none of this is guaranteed!):

fn what_data(a: &dyn Trait) -> u32 {
    unsafe {
        // `&dyn Trait` is stored as two references, the data pointer coming first and then the vtable
        // pointer. But again, this is not guaranteed!
        // On nightly we can extract the metadata with `std::ptr::metadata()`, but unfortunately it doesn't
        // give access to the full vtable, only to an opaque struct representing it :(
        let raw_dyn = std::mem::transmute::<&dyn Trait, [*const (); 2]>(a);
        // This is how the vtable is structured in memory (again, *currently*. Nothing is guaranteed!):
        #[repr(C)]
        struct Vtable {
            // First, comes the function to execute the drop glue. This is necessary for e.g. `Box<dyn Trait>`
            // to be able to drop the data correctly.
            _drop_in_place: fn(*mut ()),
            // Then, the struct size (`std::mem::size_of_val(&data)`). This is necessary for e.g. `Box<dyn Trait>`
            // to be able to correctly *deallocate* the data.
            _size: usize,
            // Then the alignment (`std::mem::align_of_val(&data)`) for the same purpose as the size.
            _align: usize,
            // And here, if you enable the unstable trait upcasting feature, comes the pointers to the supertraits'
            // vtables, so you can upcast. But we don't have supertraits, so this is empty.
            // And finally, the pointers to the methods: first supertrait methods, then the methods of the trait itself.
            // We only have one method and no supertraits, so this is easy.
            get_data: fn(*const ()) -> u32,
        }
        ((*raw_dyn[1].cast::<Vtable>()).get_data)(raw_dyn[0])
    }
}

Playground.

但是如果你真的想在你的某个项目中为此编写代码,那么可以使用@Finomennis和@prog-fh建议的闭包.

Rust相关问答推荐

计算具有相邻调换且没有插入或删除的序列的距离

即使参数和结果具有相同类型,fn的TypId也会不同

如何在不安全的代码中初始化枚举 struct

如何装箱生命周期相关联的两个对象?

如何使用 list 在Rust for Windows中编译?

为潜在的下游实现使用泛型绑定而不是没有泛型绑定的trait

你是如何在铁 rust 一侧的金牛座获得应用程序版本的?

原始数组数据类型的默认trait实现

像这样的铁 rust 图案除了‘选项’之外,还有其他 Select 吗?

为什么我们需要std::thread::scope,如果我们可以使用thread.join()在函数的生命周期内删除引用?

Rust&;Tokio:如何处理更多的信号,而不仅仅是SIGINT,即SIGQUE?

在生存期内将非静态可变引用转换为范围内的静态可变引用

如何重命名 clap_derive 中的子命令占位符?

如何从borrow 的异步代码运行阻塞代码?

在发布中包含 Rust DLL

不安全块不返回预期值

为什么 File::read_to_end 缓冲区容量越大越慢?

判断 is_ok 后重用结果

为什么拥有 i32 所有权的函数需要它是可变的?

在 Rust 中枚举字符串的最佳方式? (字符()与 as_bytes())