如何在不分配内存的情况下将 Vec 分成两部分?

8

我正在寻找类似于slice::split_at_mut的函数。我们将其命名为split_at,并具有以下签名:

pub fn split_at<T>(v: Vec<T>, mid: usize) -> (Vec<T>, Vec<T>)

以至于
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]);

该函数不应分配任何内存,只需将向量拆分为两个部分。由于生成的向量不会被修改,因此您无需担心容量。

仅夜间可用的方法Vec::into_raw_parts似乎很有前途,但我使用的是稳定版本渠道,不允许使用这样的方法。


1
编译器如何知道何时释放已分配的内存? - Shepmaster
@Shepmaster 嗯...我还没有考虑过这个问题。也许当firstsecond都超出它们的作用域时,它可以回收内存?你的问题提醒我,即使Vec::into_raw_parts已经稳定,我可能也不应该使用它,因为如果在second之前删除first,则second可能引用一个悬空指针。 - nalzok
要检测两个作用域的退出,您需要某种引用计数。然后,您只是在重新发明bytes - rodrigo
2个回答

10

按照您的要求,无法使用 Vec 实现您提出的请求。 Vec 表示对分配内存的唯一所有权。当一个 Vec 超出其作用域时,该内存将被释放。

如果您可以做到您所要求的,那么您会:

  1. 释放一块内存两次(即起始内存块)
  2. 释放一个未分配的内存块(在内存中间的某个地方)

这两种情况都是 内存不安全 的,而 Rust 正是为了防止这种情况而设计的。


您可能来自具备垃圾回收机制的编程语言,这也是在 Rust 中解决同一问题的方法之一。

Bytes crate 提供了一个类似于向量的引用计数类型,称为 Bytes(对于其他情况则称为 BytesMut):

use bytes::Bytes; // 1.0.1

fn main() {
    let v = Bytes::from(vec![1, 2, 3, 4]);
    let (first, second) = v.split_at(2);
    assert_eq!(first, vec![1, 2]);
    assert_eq!(second, vec![3, 4]);
}

吹毛求疵:由于Vec存在于堆上,技术上可能会泄漏内存,这是安全的。然而,内存泄漏肯定是不可取的。 - nalzok

7

虽然不完全符合我的需求,但split_off已经足够接近了。

let mut vec = vec![1, 2, 3];
let vec2 = vec.split_off(1);
assert_eq!(vec, [1]);
assert_eq!(vec2, [2, 3]);

回答自己的问题
pub fn split_at<T>(mut v: Vec<T>, mid: usize) -> (Vec<T>, Vec<T>) {
    let remainder = v.split_off(mid);
    (v, remainder)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接