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

如何在不分配内存的情况下拆分Vec?求实现类似slice::split_at_mut的split_at函数(稳定版可用)

如何在稳定版Rust中实现无分配的Vec拆分函数split_at

首先得明确一个核心的Rust所有权规则:Vec<T>独占一块连续堆内存的所有权,当它被销毁时会释放整个内存块。这意味着不可能安全地把一个Vec拆成两个独立的Vec,同时让它们各自管理原内存的一部分——因为两个Vec最终都会尝试释放同一块内存,触发双重释放的未定义行为。

不过如果你能接受使用unsafe代码(并且愿意承担相应风险),我们可以借助ManuallyDrop和原始指针来实现这个需求,同时严格保证安全条件。以下是具体实现:

实现代码

use std::mem::ManuallyDrop;
use std::ptr;

pub fn split_at<T>(mut v: Vec<T>, mid: usize) -> (Vec<T>, Vec<T>) {
    // 先校验mid的合法性,避免越界
    assert!(mid <= v.len(), "mid cannot exceed vector length");
    
    // 用ManuallyDrop包裹原Vec,阻止它自动释放内存
    let mut v = ManuallyDrop::new(v);
    
    // 获取原Vec的核心组件:指针、长度、容量
    let ptr = v.as_ptr();
    let len = v.len();
    let cap = v.capacity();
    
    // 构造第一个Vec,接管前mid个元素
    let first = unsafe {
        // 将第一个Vec的容量设为mid,确保它只管理前半段内存
        Vec::from_raw_parts(ptr, mid, mid)
    };
    
    // 构造第二个Vec,接管从mid开始的剩余元素
    let second = unsafe {
        // 将指针移动到mid位置
        let second_ptr = ptr.add(mid);
        // 剩余长度为原长度减mid,容量为原容量减mid
        Vec::from_raw_parts(second_ptr, len - mid, cap - mid)
    };
    
    (first, second)
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_split_at() {
        let v = vec![1, 2, 3, 4];
        let (first, second) = split_at(v, 2);
        assert_eq!(first, vec![1, 2]);
        assert_eq!(second, vec![3, 4]);
    }
}

关键安全说明

这个unsafe实现必须满足以下条件才能保证安全:

  1. mid必须在0..=v.len()范围内:我们通过assert!强制校验,避免指针越界。
  2. 原Vec被ManuallyDrop包裹:彻底阻止原Vec自动释放内存,把所有权完全转移给新生成的两个Vec。
  3. 两个新Vec的内存区域完全不重叠:第一个管理[ptr, ptr+mid),第二个管理[ptr+mid, ptr+len),没有任何重叠。
  4. 容量设置合理:第一个Vec的容量设为mid,第二个设为cap - mid,确保它们的内存操作不会超出原分配的内存块。

另外要注意:

  • 这个实现依赖Vec的内部结构(指针、长度、容量的三元组),虽然这个结构在稳定版中已经长期稳定,但理论上Rust团队未来可能修改它(概率极低)。
  • 你提到生成的向量不会被修改,这很重要——如果对返回的Vec执行push等修改长度的操作,会直接触发内存越界,因为它们的容量被限制在了各自的分段内。

安全替代方案(会分配内存)

如果你不想碰unsafe代码,唯一的安全方案是复制元素到新Vec中,虽然会分配内存,但完全符合Rust的安全规则:

pub fn split_at<T: Clone>(v: Vec<T>, mid: usize) -> (Vec<T>, Vec<T>) {
    assert!(mid <= v.len());
    let (first_slice, second_slice) = v.split_at(mid);
    (first_slice.to_vec(), second_slice.to_vec())
}

为什么稳定版没有原生安全方法

Rust稳定版API没有提供直接拆分Vec的方法,因为这涉及到内存所有权的拆分,而Rust的安全模型无法保证两个Vec不会同时尝试释放同一块内存。Nightly版的Vec::into_raw_parts虽然能获取Vec的原始组件,但即使使用它,你仍然需要手动处理内存所有权,避免双重释放,这同样离不开unsafe代码。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 20:48:13