如何以方便的方式对齐堆上的内存(在一个盒子里)?

How can I align memory on the heap (in a box) in a convenient way?

我想将我的堆内存对齐到特定的对齐边界。这个边界在编译时是已知的。无论如何,Box 抽象不允许我指定特定的对齐方式。编写我自己的 Box-抽象感觉也不对,因为所有 Rust 生态系统都已经使用 Box。实现堆分配对齐的便捷方法是什么?

PS:在我的具体情况下,我需要页面对齐。

如果 nightly 是可以接受的,Allocator api 提供了一种相当方便的方法来做到这一点:

#![feature(allocator_api)]

use std::alloc::*;
use std::ptr::NonNull;
struct AlignedAlloc<const N: usize>;


unsafe impl<const N: usize> Allocator for AlignedAlloc<N> {
    fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError> {
        Global.allocate(layout.align_to(N).unwrap())
    }
    unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout) {
        Global.deallocate(ptr, layout.align_to(N).unwrap())
    }
}

fn main() {
    let val = Box::new_in(3, AlignedAlloc::<4096>);
    let ptr: *const u8 = &*val;
    println!(
        "val:{}, alignment:{}",
        val,
        1 << (ptr as usize).trailing_zeros()
    );
}

Playground

如果需要,您还可以添加对使用其他分配器或动态选择值的支持。

编辑:想想看,这种方法也可以通过 GlobalAlloc 特性和 #[global_allocator] 属性在稳定版中使用,但是以这种方式设置分配器会强制所有分配对齐到相同的对齐方式,因此如果您需要确保相对较小的对齐方式就可以了,但是对于页面边界对齐方式来说这可能不是一个好主意。

编辑 2:从使用 System 分配器切换到 Global 分配器,因为它与 allocater_api 功能捆绑在一起,并且它是一个更明智和可预测的默认值。

根据你要对齐的数据,你可以使用repr (align)来指定一个类型的对齐方式。您可以直接在数据上使用它,也可以使用包装器结构。

例如,以下将始终按 16 字节对齐(无论是在堆上、堆栈上还是在另一个结构内):

#[repr (C, align (16))]
struct AlignedBuffer([u8; 32]);