当长度不变时,Vec::splice() 是否有效?

  • 本文关键字:splice 是否 有效 Vec rust vec
  • 更新时间 :
  • 英文 :


当你使用Vec::splice()来替换Vec的一部分时,是否足够聪明地专门处理替换与它所替换的块长度相同的情况,还是我应该自己处理?

这样做有什么意义吗,或者array.splice()足够聪明吗?

fn example(array: Vec<u64>, replacement: Vec<u64>, range: std::ops::Range<usize>) {
if (replacement.len() == range.len()) {
for i in range {
array[i] = replacement[i];
}
} else {
array.splice(range, replacement);
}
}

我屈服并阅读了代码。它足够聪明,可以处理这种情况。以下是代码的工作原理:

  1. 调用.splice(),它会创建一个Splice对象,其中包含替换范围的Drain迭代器:

    pub fn splice<R, I>(&mut self, range: R, replace_with: I) -> Splice<'_, I::IntoIter>
    where
    R: RangeBounds<usize>,
    I: IntoIterator<Item = T>,
    {
    Splice { drain: self.drain(range), replace_with: replace_with.into_iter() }
    }
    
  2. Splice被丢弃,这调用其drop()实现。

    impl<I: Iterator> Drop for Splice<'_, I> {
    fn drop(&mut self) {
    

    它做的第一件事是删除所有正在替换的元素。

    self.drain.by_ref().for_each(drop);
    

    然后它会检查替换是否在Vec末尾,如果是这样,只需扩展它并返回。

    unsafe {
    if self.drain.tail_len == 0 {
    self.drain.vec.as_mut().extend(self.replace_with.by_ref());
    return;
    }
    

    接下来,它调用一个帮助程序fill()函数,以从替换迭代器(replace_with(中尽可能多地替换已删除的元素。 如果fill()没有填满整个替换范围,false返回,在这种情况下它会返回(不过我不确定在这种情况下尾巴在哪里移动?

    // First fill the range left by drain().
    if !self.drain.fill(&mut self.replace_with) {
    return;
    }
    

    现在replace_with可能还剩下 0 个元素(在这种情况下我们完成了(,或者它可能有更多的元素(在这种情况下,尾巴需要向后移动该数量(。这就是接下来发生的事情。

    // There may be more elements. Use the lower bound as an estimate.
    // FIXME: Is the upper bound a better guess? Or something else?
    let (lower_bound, _upper_bound) = self.replace_with.size_hint();
    if lower_bound > 0 {
    self.drain.move_tail(lower_bound);
    if !self.drain.fill(&mut self.replace_with) {
    return;
    }
    }
    

    您可能期望if lower_bound == 0 { return; },但lower_bound只是一个估计值,因此它首先尝试使用它,如果失败,它将替换复制到临时向量中,因此它可以知道完整的长度。

    // Collect any remaining elements.
    // This is a zero-length vector which does not allocate if `lower_bound` was exact.
    let mut collected = self.replace_with.by_ref().collect::<Vec<I::Item>>().into_iter();
    // Now we have an exact count.
    if collected.len() > 0 {
    self.drain.move_tail(collected.len());
    let filled = self.drain.fill(&mut collected);
    debug_assert!(filled);
    debug_assert_eq!(collected.len(), 0);
    }
    

    关键是,如果replace_with空,因为替换大小与范围相同,那么所有这些操作都是相当简单的 nop 之类的事情。

    最后一位丢弃Drain迭代器本身,这将移动尾巴......可能再次移动?我不知道我在这里有点迷失了踪迹。无论如何,如果长度相同,它绝对不会移动它。

    }
    // Let `Drain::drop` move the tail back if necessary and restore `vec.len`.
    }
    }
    

根据文档和另一个答案,splice在某些情况下是"聪明的"(最佳(,包括"[replace_with]size_hint()的下限是精确的"。

在示例函数中,replacement.iter()上的size_hint()下限是精确的,因此应执行一次内存移动。

据我所知,Splice::drop是完成大部分工作的原因,这调用fill,在匹配长度的情况下,它将遍历replace_with,并返回truereplace_with为空,因此lower_bound为 0,collected.len()为 0。

然后Drain::drop运行。循环不应该做任何事情,因为Splice::drop已经迭代了它,然后DropGuard::drop,可能需要移动元素,不应该需要移动/复制元素,因为长度匹配的结果,tail应该等于start

相关内容

  • 没有找到相关文章

最新更新