Rust裸指针元数据含义及DST元数据相关技术疑问
Rust裸指针元数据(Raw Pointer Metadata)详解
问题背景
我想了解Rust中的Raw pointer metadata是什么,同时有以下困惑:为什么Box中的裸指针元数据是(),但栈上的sized类型转为动态类型后,其元数据是slice([T])的元素数量(类型为usize,符合Pointee文档描述)。我编写了测试代码,但实际结果和预期不符,同时希望能有更多示例解释DST(动态大小类型)的元数据。
测试代码如下:
#![feature(box_as_ptr)] #![feature(ptr_metadata)] #![allow(unused)] struct MySuperSliceable<T: ?Sized> { info: u32, data: T, } fn main() { let sized: MySuperSliceable<[u8; 8]> = MySuperSliceable { info: 17, data: [0; 8], }; let box_sized = Box::new(MySuperSliceable { info: 18, data: [0; 12], }); let dynamic: &MySuperSliceable<[u8]> = &sized; let box_dynamic = Box::as_ptr(&box_sized); assert_eq!(std::ptr::metadata(dynamic), 8_usize); assert_eq!(std::ptr::metadata("hello, world!"), 13_usize); let a = 10; assert_eq!(std::ptr::metadata(&a), ()); assert_eq!(std::ptr::metadata(box_dynamic), ()); assert_eq!(std::ptr::metadata(box_sized.as_ref()), ()); }
我原本认为最后两个断言应该返回12_usize,但实际结果是(),这和预期不符。
核心概念:Raw Pointer Metadata
Rust中指针分为两类:
- 瘦指针:指向固定大小类型(Sized Type)的指针,仅包含内存地址,元数据为
()——因为固定大小类型的内存占用是编译期确定的,不需要额外信息。 - 胖指针:指向动态大小类型(DST)的指针,由「内存地址 + 元数据」两部分组成。元数据的类型和内容由
Pointeetrait定义,不同DST的元数据规则不同:- 切片类型(
[T])的元数据是切片的长度(usize) - trait对象(
dyn Trait)的元数据是虚表(vtable)的指针 - 自定义DST的元数据由其内部的DST成员决定(比如
MySuperSliceable<[u8]>的元数据就是内部[u8]的长度)
- 切片类型(
代码困惑解析
问题出在box_sized的类型判定上:
box_sized的类型是Box<MySuperSliceable<[u8; 12]>>,其中[u8;12]是固定大小数组(Sized类型),因此整个MySuperSliceable<[u8;12]>属于固定大小类型。Box::as_ptr(&box_sized)返回的是*const MySuperSliceable<[u8;12]>,这是一个瘦指针,元数据自然为()。box_sized.as_ref()得到的&MySuperSliceable<[u8;12]>同样是指向固定大小类型的引用,元数据也是()。
如果要得到预期的12_usize,需要将box_sized转换为指向DST类型的Box:
// 将sized类型的Box转换为DST类型的Box let box_dynamic: Box<MySuperSliceable<[u8]>> = box_sized; // 此时的裸指针是胖指针,元数据为slice长度12 assert_eq!(std::ptr::metadata(Box::as_ptr(&box_dynamic)), 12_usize); assert_eq!(std::ptr::metadata(box_dynamic.as_ref()), 12_usize);
转换后,Box持有MySuperSliceable<[u8]>这个DST类型,对应的指针就是胖指针,元数据就是内部slice的长度。
DST元数据额外示例
1. 切片类型的元数据
#![feature(ptr_metadata)] fn main() { let arr = [10, 20, 30]; let slice: &[i32] = &arr[0..2]; // 元数据是切片的长度2 assert_eq!(std::ptr::metadata(slice), 2_usize); }
2. Trait对象的元数据
#![feature(ptr_metadata)] trait Shape { fn area(&self) -> f64; } struct Circle { radius: f64 } impl Shape for Circle { fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius } } fn main() { let circle = Circle { radius: 2.0 }; let shape: &dyn Shape = &circle; // 元数据是vtable指针,可打印其地址 let vtable_ptr = std::ptr::metadata(shape); println!("Trait object vtable address: {:p}", vtable_ptr); }
3. 自定义DST的元数据
#![feature(ptr_metadata)] struct Wrapper<T: ?Sized> { tag: u64, inner: T, } fn main() { let fixed = Wrapper { tag: 123, inner: [true; 4] }; let dyn_wrap: &Wrapper<[bool]> = &fixed; // 元数据是内部切片的长度4 assert_eq!(std::ptr::metadata(dyn_wrap), 4_usize); }
内容的提问来源于stack exchange,提问作者Yexuan Yang
相关产品推荐
相关产品推荐

