简体   繁体   English

如何获取特征 object 的地址?

[英]How to obtain address of trait object?

How do you obtain address of trait object?您如何获得特征 object 的地址? I tried this:我试过这个:

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
}

But it yields different addresses.但它产生不同的地址。

First, the easy one: as mentioned in the comments, &boxed_func is simply the address of the local variable boxed_func , not the address of the underlying data.首先,简单的一个:正如评论中提到的, &boxed_func只是局部变量boxed_func的地址,而不是底层数据的地址。 Think of it as a pointer to the pointer.将其视为指向指针的指针。

Now the hard part.现在是困难的部分。 I think one thing that's confusing you is that pointers to trait objects are fat pointers , and this isn't reflected by printing with "{:p}" .我认为让您感到困惑的一件事是指向特征对象的指针是胖指针,而使用"{:p}"打印并没有反映出这一点。 They consist of a pointer to the actual data as well as a pointer to a vtable, which stores information about the implementation of the trait.它们由指向实际数据的指针和指向 vtable 的指针组成,vtable 存储有关 trait 实现的信息。

This can be seen with the (likely UB) code这可以通过(可能是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) (游乐场链接)

So the actual underlying pointer of boxed_func consists of two pointers: 0x1 and 0x55ec289004c8 (results may vary here).所以boxed_func的实际底层指针由两个指针组成: 0x10x55ec289004c8 (结果可能在此处有所不同)。 0x1 is the usual value for pointers to zero-sized types. 0x1是指向零大小类型的指针的常用值。 Obviously, you don't want to use a null pointer for this purpose, but you don't really need a valid pointer, either.显然,您不想为此目的使用 null 指针,但您也不需要有效指针。 Zero-sized types are often allocated usingUnique::empty , which simply returns a dangling pointer to the memory location at the alignment of the type (the alignment of a zero-sized type is 1).零大小的类型通常使用Unique::empty分配,它简单地返回一个悬空指针,指向位于该类型的 alignment 的 memory 位置(Z5A159214B169DA90D96590D47F139 的 Z5A159214B169DA90D96590D47F139 类型)。

// 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) (游乐场链接)

So in our situation with the trait object, this is telling us that the data part of the trait object is (probably) a zero-sized type, which makes sense since func doesn't have any data associated to it other than what's needed to call it.因此,在我们的特征 object 的情况下,这告诉我们特征 object 的数据部分(可能)是零大小的类型,这是有道理的,因为func除了需要的数据之外没有任何与之关联的数据叫它。 That information is kept in the vtable .该信息保存在vtable中。


A safer (less UB inducing) way to see the raw parts of the trait object is with the nightly-only struct TraitObject .查看特征 object 的原始部分的一种更安全(更少 UB 诱导)的方法是使用仅夜间结构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) (游乐场链接)

Try this out with some other trait objects and see if you can find some that don't have zero-sized data.用其他一些特征对象试试这个,看看你是否能找到一些没有零大小数据的对象。


TraitObject is deprecated and according to Tracking Issue for pointer metadata APIs #81513 , it's replaced with to_raw_parts(self) method of primitive type pointer . TraitObject已弃用,并且根据Tracking Issue for pointer metadata APIs #81513 ,它已被原始类型指针的to_raw_parts(self)方法替换。


#![feature(ptr_metadata)]

trait Trait {
    fn f(&self) -> i32;
}

struct Struct {
    i: i32
}

impl Trait for Struct {
    fn f(&self) -> i32 {
        self.i
    }
}

fn main() {
    let s = Struct { i: 1 };
    let sp = &s as *const _;
   
    let (sdynp, sdynvtable) = (&s as &dyn Trait as *const dyn Trait).to_raw_parts();
    
    println!("sp = {:p}", sp);
    
    println!("sdynp = {:p}, sdynvtable = {:#?}", sdynp, sdynvtable);
}

playground link 游乐场链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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