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

如何在Rust中安全合并内存中相邻的切片?

安全合并内存中相邻的Rust切片

问题背景

我有两个通过split_at方法从同一切片拆分得到的相邻内存切片,希望能安全地将它们合并回覆盖原范围的切片,且不复制任何数据。理想情况下能有一个通用函数,可以接收多个同类型切片,自动检测它们是否在内存中连续相邻,在可行时合并成一个大切片。

比如下面的示例代码,需要实现todo!部分的逻辑:

fn main() {
    let original_slice: &[u8] = &[1, 2, 3, 4];
    
    let (left, right) = original_slice.split_at(2);
    
    let rejoined_slice = todo!("take left and right and return an Option or similar");
    
    assert_eq!(original_slice, rejoined_slice);
    assert_eq!(original_slice.as_ptr(), rejoined_slice.as_ptr());
}

注:此需求不同于普通的切片拼接(那种会生成新Vec),我需要的是原地合并已相邻的切片,适用于zerocopy这类零拷贝解析场景。


安全实现方案

Rust的安全规则不允许直接构造跨内存块的切片,因此我们需要先手动验证切片的连续性,再通过unsafe代码构造合并后的切片(但验证步骤能确保操作的安全性)。

1. 合并两个相邻切片

先实现针对两个切片的合并函数,核心是验证右切片的起始地址等于左切片的结束地址,且两者生命周期一致:

fn try_join_two_slices<T>(left: &[T], right: &[T]) -> Option<&[T]> {
    // 检查右切片起始指针是否等于左切片的结束指针
    let left_end = left.as_ptr().wrapping_add(left.len());
    if left_end != right.as_ptr() {
        return None;
    }
    
    // 构造合并后的切片,这里unsafe是安全的,因为已验证连续性
    unsafe {
        Some(std::slice::from_raw_parts(left.as_ptr(), left.len() + right.len()))
    }
}

2. 合并多个相邻切片

扩展到多个切片的场景,需要依次检查每一对相邻切片是否连续:

fn try_join_slices<T>(slices: &[&[T]]) -> Option<&[T]> {
    if slices.is_empty() {
        return Some(&[]);
    }
    
    // 先获取第一个切片的起始地址和总长度
    let mut start_ptr = slices[0].as_ptr();
    let mut total_len = slices[0].len();
    
    for window in slices.windows(2) {
        let prev = window[0];
        let curr = window[1];
        
        let prev_end = prev.as_ptr().wrapping_add(prev.len());
        if prev_end != curr.as_ptr() {
            return None;
        }
        
        total_len += curr.len();
    }
    
    // 构造合并后的切片
    unsafe {
        Some(std::slice::from_raw_parts(start_ptr, total_len))
    }
}

示例验证

把上述函数用到最初的示例中:

fn main() {
    let original_slice: &[u8] = &[1, 2, 3, 4];
    
    let (left, right) = original_slice.split_at(2);
    
    // 合并两个切片
    let rejoined_slice = try_join_two_slices(left, right).unwrap();
    
    assert_eq!(original_slice, rejoined_slice);
    assert_eq!(original_slice.as_ptr(), rejoined_slice.as_ptr());
    
    // 测试多个切片的情况
    let (a, rest) = original_slice.split_at(1);
    let (b, c) = rest.split_at(1);
    let multi_rejoined = try_join_slices(&[a, b, c]).unwrap();
    assert_eq!(original_slice, multi_rejoined);
}

关键说明

  • 为什么需要unsafe?因为Rust编译器无法在安全代码中证明这些切片属于同一连续内存块,也无法证明合并后的切片不会越界访问。但我们通过指针检查验证了连续性,且所有输入切片都是合法的引用,因此unsafe操作是安全的。
  • 该方案完全符合零拷贝需求,合并过程不会复制任何数据,只是重新构造了一个覆盖连续内存范围的切片引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 21:10:36