删除可变Vec中前N个元素的惯用方法是什么?

时间:2017-03-26 07:16:32

标签: vector rust

有没有一种很好的方法可以在向量的开头删除多个元素?

我想避免多次删除,这会导致不必要的内存复制操作。

while vec.len() > n {
    vec.remove(0);
}
  1. 我可以使用不安全的API(ptr::drop_in_placeptr::copyVec::set_len),但希望有更方便的方法来处理这个问题。

  2. Vec指针偏移且开始时的范围被标记为空闲时,是否可以使用替代解决方案? (无需复制内存)。 我现在意识到这需要Rust的底层分配器来释放内存而不是最初分配的指针,但事实并非如此。

2 个答案:

答案 0 :(得分:13)

使用drain尽可能高效地从矢量中删除多个连续元素(implementation使用ptr::copy移动剩余的元素):

let mut v = vec![1, 2, 3, 4];
v.drain(0..2);
assert!(v == vec![3, 4]);

关于#2,避免移动其余元素是不可行的。该优化需要更改向量的表示,更改分配器或两者,并且对于用例设计用于覆盖的用例。如果您需要从前面有效删除,请使用VecDeque

Vec由包含< pointer-to-first-element, capacitylength>的三元组表示。如果从前面移除避免通过向前移动开始指针来移动剩余元素,则解除分配将会崩溃,因为启动指针将不再是分配器提供的指针。向量需要获得"原始指针"的新字段,这将使所有向量为优化付费,或者需要使用释放块开头的方法扩展分配接口。 每次正面删除都会调用分配器,因为分配器必须执行自己的簿记并可能获取锁定,否则会产生难以评估的性能影响。它还会给分配器增加负担,要求它跟踪位于它最初传送的块之前的这些可能很小的未对齐空闲块。它还会使向量与几乎所有本机分配器不兼容,例如malloc()mmap()

最后,优化将与Vec当前提供的保证相矛盾。缩小Vec的文档explicitly states不会降低其容量或取消分配,除非调用shrink_to_fit。这是为了避免对收缩和增长很多的向量进行过度分配。

答案 1 :(得分:2)

如果您一心想直接使用Vec,那么@user4815162342会解释为什么您要求的是不可能的。

但是,如果(1)你真的需要从前面有效删除和(2)邻接问题,那么VecDeque是不合适的,那么也可以建立你自己的容器。我曾经在C ++中有过这两(2)个严格的要求,在这种情况下,C ++比Rust要繁琐得多。

基本结构是:

struct DVec<T> {
    data: *mut T,
    length: usize,
    offset: usize,
    capacity: usize,
    _marker: PhantomData<T>,
}

根据您的要求,您通常可以使用u32代替usize(仍允许40亿个元素!)缩小尺寸。

之后,DVec::drain(..n)DVec::drain(0..n)通过offset碰撞n来实现。

此数据结构提供类似于VecDeque的界面,并进行了以下更改:

  • 数据是连续存储的,因此它实现了Deref<Target = [T]>
  • 在任一端插入元素摊销 O(1),
  • 在任一端去除元素是O(1)。

在C ++中,编写是很痛苦的,因为并非所有对象都是可移动的,移动会导致异常;在Rust中,所有对象都是可移动的,移动是无异常的,所以它相当简单。