You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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)的指针,由「内存地址 + 元数据」两部分组成。元数据的类型和内容由Pointee trait定义,不同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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 05:43:18