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

如何在Rust的Arc中创建含自有切片的动态大小结构体?

在Rust的Arc中创建包含自有切片的动态大小结构体

你的核心需求是在Arc中直接存储一个包含usize和自有切片的动态大小类型(DST),且仅保留一层间接引用。原代码无法运行的原因是:Arc::new要求参数实现Sized trait,而末尾带有[u8]的UnknownSized是动态大小类型,不符合要求。

以下是不依赖外部库的实现方案,通过手动管理内存和裸指针操作来实现需求:

实现方案一:基于alloc库手动分配内存

use std::sync::Arc;
use std::alloc::{alloc, dealloc, Layout};
use std::ptr;

// 动态大小结构体,末尾字段为[u8]以实现DST
struct UnknownSized(usize, [u8]);

impl UnknownSized {
    /// 创建包含指定数据的Arc<UnknownSized>
    fn new(attached_data: usize, data: &[u8]) -> Arc<Self> {
        // 计算内存布局:固定部分(usize) + 切片数据,确保对齐要求
        let (layout, _) = Layout::new::<usize>()
            .extend(Layout::array::<u8>(data.len()).unwrap())
            .unwrap();

        // 分配内存,处理分配失败的情况
        let ptr = unsafe { alloc(layout) };
        if ptr.is_null() {
            std::alloc::handle_alloc_error(layout);
        }

        unsafe {
            // 写入固定部分数据
            ptr::write(ptr as *mut usize, attached_data);
            // 将切片数据复制到固定部分后的内存区域
            ptr::copy_nonoverlapping(data.as_ptr(), ptr.add(1) as *mut u8, data.len());

            // 将裸指针转换为Arc<UnknownSized>
            Arc::from_raw(ptr as *const UnknownSized)
        }
    }
}

// 实现Drop trait,确保内存正确释放
impl Drop for UnknownSized {
    fn drop(&mut self) {
        let data_len = self.1.len();
        let (layout, _) = Layout::new::<usize>()
            .extend(Layout::array::<u8>(data_len).unwrap())
            .unwrap();
        unsafe {
            dealloc(self as *mut Self as *mut u8, layout);
        }
    }
}

fn main() {
    let attached_data: usize = 42;
    let data: Vec<u8> = vec![1, 2, 3, 4, 5]; 

    let arc_object = UnknownSized::new(attached_data, &data);

    // 验证数据正确性
    assert_eq!(arc_object.0, 42);
    assert_eq!(&arc_object.1, &[1,2,3,4,5]);
}

实现方案二:利用Vec辅助分配内存(更简洁)

借助Vec的内存管理能力处理对齐和分配,减少手动内存操作的复杂度:

use std::sync::Arc;
use std::ptr;

struct UnknownSized(usize, [u8]);

impl UnknownSized {
    fn new(attached_data: usize, data: Vec<u8>) -> Arc<Self> {
        // 分配足够容纳usize+切片数据的内存
        let total_size = std::mem::size_of::<usize>() + data.len();
        let mut vec = Vec::with_capacity(total_size);

        unsafe {
            // 写入固定部分数据
            vec.push(std::mem::transmute_copy(&attached_data));
            // 写入切片数据
            vec.extend_from_slice(&data);
            
            // 获取指针并阻止Vec自动释放内存
            let ptr = vec.as_ptr();
            std::mem::forget(vec);

            // 将指针转换为Arc<UnknownSized>
            Arc::from_raw(ptr as *const UnknownSized)
        }
    }
}

impl Drop for UnknownSized {
    fn drop(&mut self) {
        let data_len = self.1.len();
        let total_size = std::mem::size_of::<usize>() + data_len;
        unsafe {
            // 将指针转回Vec,利用Vec的Drop逻辑自动释放内存
            let vec = Vec::from_raw_parts(self as *mut Self as *mut u8, total_size, total_size);
            std::mem::drop(vec);
        }
    }
}

fn main() {
    let attached_data: usize = 42;
    let data: Vec<u8> = vec![1, 2, 3, 4, 5]; 

    let arc_object = UnknownSized::new(attached_data, data);

    assert_eq!(arc_object.0, 42);
    assert_eq!(&arc_object.1, &[1,2,3,4,5]);
}

注意事项

两种方案都使用了unsafe代码,需确保以下内存安全要求:

  • 结构体布局必须符合Rust的DST规则:动态大小字段必须是结构体的最后一个字段
  • 内存分配和释放的布局必须完全匹配,避免内存泄漏或UAF
  • 数据写入时必须保证类型安全,无越界访问

内容的提问来源于stack exchange,提问作者HelloWorld2457

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 05:34:53