如何将自定义u16长度容量的C Vec结构体转为Rust并保持内存布局?
实现与给定C代码内存布局一致的自定义小型Vec
要在Rust里实现和你提供的C结构体完全一致的内存布局,核心是用#[repr(C)]强制编译器遵循C的字段排布规则,避免Rust默认的优化重排。以下是完整的实现代码:
use std::alloc::{alloc, dealloc, Layout}; use std::ptr; // 用#[repr(C)]保证内存布局和C结构体完全匹配 #[repr(C)] struct MyVec { len: u16, capacity: u16, ptr: *mut u8, } impl MyVec { // 创建空的自定义Vec pub fn new() -> Self { Self { len: 0, capacity: 0, ptr: ptr::null_mut(), } } // 预分配指定容量的内存(容量不能超过u16最大值65535) pub fn with_capacity(cap: u16) -> Self { if cap == 0 { return Self::new(); } // 计算u8数组的内存布局 let layout = Layout::array::<u8>(cap as usize).expect("无效的内存布局"); unsafe { let ptr = alloc(layout) as *mut u8; Self { len: 0, capacity: cap, ptr, } } } // 向Vec中添加元素(必须保证当前长度小于容量,否则会触发调试断言) pub unsafe fn push(&mut self, value: u8) { debug_assert!(self.len < self.capacity, "Vec容量不足"); ptr::write(self.ptr.add(self.len as usize), value); self.len += 1; } // 获取指定索引的元素引用(索引必须小于当前长度) pub unsafe fn get(&self, idx: u16) -> &u8 { debug_assert!(idx < self.len, "索引越界"); &*self.ptr.add(idx as usize) } // 获取指定索引的可变元素引用(索引必须小于当前长度) pub unsafe fn get_mut(&mut self, idx: u16) -> &mut u8 { debug_assert!(idx < self.len, "索引越界"); &mut *self.ptr.add(idx as usize) } // 从标准Vec<u8>转换为自定义Vec(原Vec长度和容量不能超过65535) pub fn from_vec(mut vec: Vec<u8>) -> Self { let len = vec.len() as u16; let capacity = vec.capacity() as u16; let ptr = vec.as_mut_ptr(); // 阻止标准Vec的drop逻辑释放内存 std::mem::forget(vec); Self { len, capacity, ptr } } // 将自定义Vec转换回标准Vec<u8> pub fn into_vec(self) -> Vec<u8> { unsafe { let vec = Vec::from_raw_parts(self.ptr, self.len as usize, self.capacity as usize); // 阻止自定义Vec的drop逻辑重复释放内存 std::mem::forget(self); vec } } } // 实现Drop trait,确保内存被正确释放 impl Drop for MyVec { fn drop(&mut self) { if self.capacity > 0 && !self.ptr.is_null() { let layout = Layout::array::<u8>(self.capacity as usize).expect("无效的内存布局"); unsafe { dealloc(self.ptr as *mut u8, layout); } } } }
核心要点
#[repr(C)]是保证内存布局一致的关键:结构体字段会严格按照定义顺序排布,和你提供的C结构体完全对齐。- 内存节省效果:64位系统下,标准
Vec<u8>的元数据占24字节(3个usize),而MyVec仅占12字节(2个u16+1个指针),每个内层Vec能省12字节。当外层Vec有1G个元素时,总共能节省12GB内存,完全符合你的需求。 - 安全注意:所有涉及裸指针的操作都标记为
unsafe,你需要自行保证:- 调用
push时,当前长度必须小于容量,避免越界写入。 - 访问元素时,索引必须小于当前长度,避免越界读取。
- 转换标准Vec时,原Vec的长度和容量不能超过
u16::MAX(65535),否则会发生数值截断,导致内存错误。
- 调用
扩展提示
如果需要支持u8以外的元素类型,可以把MyVec改成泛型结构体,但要注意元素的内存对齐要求,调整Layout的计算逻辑。
内容的提问来源于stack exchange,提问作者Moold
相关产品推荐
相关产品推荐

