[英]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
}
So the actual underlying pointer of boxed_func
consists of two pointers: 0x1
and 0x55ec289004c8
(results may vary here).所以boxed_func
的实际底层指针由两个指针组成: 0x1
和0x55ec289004c8
(结果可能在此处有所不同)。 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
}
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
}
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);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.