rust - 如何获得特征对象的地址?

标签 rust

如何获得特征对象的地址?我尝试了这个:

fn func() {}

fn main() {
    let boxed_func: Box<dyn Fn()> = Box::new(func);

    println!("expect: {:p}", func as *const ()); // 0x55bb0207e570

    println!("actual: {:p}", &boxed_func); // 0x7ffe5217e5a0
    println!("actual: {:p}", Box::into_raw(boxed_func)); // 0x1
}

但是它产生了不同的地址。

最佳答案

首先,一个简单的方法:如注释中所述,&boxed_func只是局部变量boxed_func的地址,而不是基础数据的地址。可以将其视为指向该指针的指针。

现在是困难的部分。我认为让您感到困惑的一件事是,指向特质对象的指针是粗大的指针,而使用"{:p}"进行打印并不能反射(reflect)这一点。它们由指向实际数据的指针和指向vtable的指针组成,该表存储有关特征实现的信息。

这可以通过(可能是UB)代码看到

fn func() {}

fn main() {
    let boxed_func: Box<dyn Fn()> = Box::new(func);

    println!("function pointer: {:p}", func as fn()); // 0x560ae655d1a0
    println!("trait object data pointer: {:p}", boxed_func); // 0x1
    println!("stack pointer: {:p}", &boxed_func); // 0x7ffebe8f4688

    let raw = Box::into_raw(boxed_func);
    println!("raw data pointer: {:p}", raw); // 0x1

    // This is likely undefined behavior, since I believe the layout of trait objects isn't specified
    let more_raw = unsafe { std::mem::transmute::<_, (usize, usize)>(raw) };
    println!("full fat pointer: {:#x}, {:#x}", more_raw.0, more_raw.1); // 0x1, 0x560ae6789468
}

(playground link)

因此,boxed_func的实际基础指针由两个指针组成:0x10x55ec289004c8(结果可能会有所不同)。 0x1是指向零大小类型的指针的常用值。显然,您不想为此目的使用空指针,但是您也确实不需要有效的指针。零大小的类型通常使用 Unique::empty 分配,它会在类型的对齐方式(零大小的类型的对齐方式为1)上将悬空指针简单地返回到内存位置。

// Some zero-sized types and where they get allocated

struct Foo;

fn main() {
    let x = Box::new(());
    println!("{:p}", x); // 0x1

    let y = Box::new(Foo);
    println!("{:p}", y); // 0x1
}

(playground link)

因此,在我们使用trait对象的情况下,这告诉我们trait对象的数据部分是(可能是)零大小的类型,这是有道理的,因为func除了需要执行的操作外没有任何与之关联的数据。称它为。该信息保存在vtable中。

一种查看trait对象原始部分的更安全(减少UB诱导)的方法是使用仅夜间的struct TraitObject

#![feature(raw)]
use std::raw::TraitObject;

fn func() {}

fn main() {
    let boxed_func: Box<dyn Fn()> = Box::new(func);

    println!("function: {:p}", func as fn()); // 0x56334996e850
    println!("function trait object: {:p}", boxed_func); // 0x1
    println!("stack address: {:p}", &boxed_func); // 0x7ffee04c2378

    // Safety: `Box<dyn Trait>` is guaranteed to have the same layout as `TraitObject`.
    let trait_object = unsafe { std::mem::transmute::<_, TraitObject>(boxed_func) };
    println!("data pointer: {:p}", trait_object.data); // 0x1
    println!("vtable pointer: {:p}", trait_object.vtable); // 0x563349ba3068
}

(playground link)

将其与其他一些特征对象一起尝试,看看是否可以找到一些没有零尺寸数据的对象。

关于rust - 如何获得特征对象的地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61378906/

相关文章:

rust - 主要方法内的外部 crate ; module::Type 与 main::module::Type

c - C 语言中 Scala 的 Option 或 Rust 的 Result 错误处理

rust - 是否可以声明 2 个相互依赖的静态可变变量?

generics - 如果我在函数内部创建引用,如何将泛型类型与需要生命周期参数的特征绑定(bind)?

vector - 如何打印 Vec 中每个元素的索引和值?

rust - 用 "mean"方法扩展 Iterator

rust - 如何在Cargo中为附属关系 'pass down'功能标记?

generics - 指定泛型参数属于一小组类型

rust - “error: expected item, found ' 让 '”

rust - cargo 总是以 "Blocking waiting for file lock on build directory"开头