如何获取类型的对齐要求?

解读

在国内 Rust 岗位面试中,对齐(alignment)是内存布局与性能优化的必考点。面试官不仅想知道“能不能拿到”,更想确认你:

  1. 知道对齐对缓存行、SIMD、系统调用的影响;
  2. 能在const 上下文里使用;
  3. 能区分类型对齐值实例地址对齐这两个概念;
  4. 能结合repr 属性解释为什么 Rust 结构体对齐可能与 C 不同。

一句话:答出 align_of 只是及格,能推导出对齐推导规则并给出编译期与运行期两套方案才算亮点。

知识点

  1. 编译期获取core::mem::align_of::<T>() → 返回 usizeconst fn,可用于 staticconst
  2. 运行期获取core::mem::align_of_val(&val) → 对 DST 也有效,返回动态实际对齐
  3. 对齐约束
    • 永远大于等于 12 的幂
    • #[repr(align(N))]人工放大对齐;
    • #[repr(packed)]强制设为 1,但可能产生未对齐访问
  4. 与 size 的关系size % align == 0,Rust 自动填充 padding保证数组元素对齐。
  5. FFI 场景:与 C 交互时必须保证 align_of::<T>() == _Alignof(T),否则 bindgen 会生成编译错误
  6. 零大小类型align_of::<()>() 返回 1 而非 0,保证地址计算合法性

答案

use core::mem;

// 编译期拿到类型 T 的对齐要求
const ALIGN: usize = mem::align_of::<T>();

// 运行期拿到值 val 的对齐要求(对 trait object 也适用)
let align = mem::align_of_val(&val);

// 若需断言 FFI 安全
#[repr(C)]
struct Header { magic: u32, body: u64 }

const _: () = assert!(mem::align_of::<Header>() == 8);

要点背诵

  • align_of零成本 const fn,编译期直接替换为常量;
  • size_of 配对使用,可手动实现布局感知的分配器;
  • 嵌入式裸机中,常利用 align_of 检查 DMA 缓冲区是否满足 4/8/16 字节对齐,否则总线错误

拓展思考

  1. 自定义分配器场景:实现一个 Arena<T>,要求每个槽地址满足 align_of::<T>() 倍数。若 T#[repr(align(64))] 的缓存行对齐字段,如何在 const fn 里预计算槽大小并拒绝非法请求?
  2. SIMD 优化std::simd::Simd<f32, 8> 的对齐是 32 字节。如何结合 align_ofstd::alloc::Layout 手动分配32 字节对齐的堆缓冲区,并证明未对齐加载会导致段错误
  3. DST 动态对齐trait Packet {} 的实现类型对齐各异。如何在一个 &dyn Packet 切片里,用 align_of_val常数时间内求出最大对齐,从而保证后续批量分配的地址都能满足最严格对齐
  4. ABI 兼容性:在国产 RISC-V 内核上,硬件仅支持 4 字节对齐的 64 位浮点访存。若 Rust 侧 align_of::<f64>() 返回 8,如何在不修改源码的前提下,通过build.rs 注入 #[repr(align(4))] 并验证性能回退可接受?

把上述四点任选一个讲透,即可让面试官确信你不仅会用 align_of,更能凭它解决底层工程难题