如何获取trait对象的地址? [英] How to obtain address of trait object?

查看:60
本文介绍了如何获取trait对象的地址?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何获取trait对象的地址?我试过这个:

How do you obtain address of trait 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
}

但它产生不同的地址.

推荐答案

首先,简单一点:正如评论中提到的,&boxed_func 只是局部变量 的地址boxed_func,不是底层数据的地址.将其视为指向指针的指针.

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. Think of it as a pointer to the pointer.

现在是困难的部分.我认为让您感到困惑的一件事是,指向 trait 对象的指针是 fat 指针,这并不能通过使用 "{:p}" 打印来反映.它们由一个指向实际数据的指针和一个指向 vtable 的指针组成,vtable 存储有关 trait 实现的信息.

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}". 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.

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

This can be seen with the (likely UB) code

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
}

(游乐场链接)

所以boxed_func 的实际底层指针由两个指针组成:0x10x55ec289004c8(这里的结果可能会有所不同).0x1 是指向零大小类型的指针的常用值.显然,您不想为此使用空指针,但您也不需要一个有效的指针.零大小类型通常使用 分配Unique::empty,它只返回一个悬空指针,指向类型对齐处的内存位置(零大小类型的对齐为 1).

So the actual underlying pointer of boxed_func consists of two pointers: 0x1 and 0x55ec289004c8 (results may vary here). 0x1 is the usual value for pointers to zero-sized types. Obviously, you don't want to use a null pointer for this purpose, but you don't really need a valid pointer, either. Zero-sized types are often allocated using Unique::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).

// 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
}

(游乐场链接)

所以在我们使用 trait 对象的情况下,这告诉我们 trait 对象的数据部分(可能)是一个零大小的类型,这是有道理的,因为 func 没有与其相关联的任何数据,而不是调用它所需的数据.该信息保存在 vtable 中.

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. That information is kept in the vtable.

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

A safer (less UB inducing) way to see the raw parts of the trait object is with the nightly-only 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
}

(游乐场链接)

用其他一些特征对象试试这个,看看你是否能找到一些没有零大小数据的对象.

Try this out with some other trait objects and see if you can find some that don't have zero-sized data.

TraitObject 已弃用,根据 跟踪指针问题元数据 API #81513,它被替换为 to_raw_parts(self) 原始类型指针的方法.

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.


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

游乐场链接

这篇关于如何获取trait对象的地址?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆