Rust 当长度不为';你不会变吗?

Rust 当长度不为';你不会变吗?,rust,vec,Rust,Vec,当您使用替换Vec的一部分时,是否明智地专门处理替换长度与其替换的块长度相同的情况,还是我应该自己处理 这样做有什么意义,或者array.splice()聪明到可以这样做吗 fn example(array: Vec<u64>, replacement: Vec<u64>, range: std::ops::Range<usize>) { if (replacement.len() == range.len()) { for i in

当您使用替换
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);
    }
}
fn示例(数组:Vec,替换:Vec,范围:std::ops::range){
if(replacement.len()==range.len()){
因为我在射程内{
数组[i]=替换[i];
}
}否则{
阵列拼接(范围、替换);
}
}
根据和,
拼接
在一些情况下是“智能”(最佳)的,包括“将替换为]的大小提示()的下限是精确的。”

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

据我所知,这是大部分工作的内容,它调用
fill
,在长度匹配的情况下,它将通过
replace\u进行迭代,并返回
true
但将
replace\u保留为空,因此
下限
为0,而
collected.len()
为0


然后跑。循环不应该做任何事情,因为
Splice::drop
已经在它上面进行了迭代,然后
DropGuard::drop
,它可能需要移动元素,不应该移动/复制元素,因为由于长度匹配,
tail
应该等于
start
我放弃并读取了代码。处理那个案子很聪明。下面是代码的工作原理:

  • 调用
    .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() }
    }
    
    它所做的第一件事就是删除所有被替换的元素

            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;
                }
    
    接下来,它调用一个helper
    fill()
    函数,用替换迭代器中尽可能多的元素替换删除的元素(
    replace_with
    fill()
    返回
    false
    如果它没有填充整个替换范围,在这种情况下它会返回(但我不确定在这种情况下尾巴会移动到哪里?)

    现在,
    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;
                    }
                }
    
    如果下限==0{return;}
  • ,您可能会期望
    ,但是
    下限
    只是一个估计值,因此它首先尝试使用该值,如果失败,它会将替换复制到一个临时向量中,这样它就可以知道完整长度

                // 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);
                }
    

    具体来说,你指的是哪个“高效”和“足够聪明”?您是否关心内存分配,或者使用SIMD扩展来复制数据,或者多次移动内存,或者……您可以尝试查看源代码:,并试图避免这种情况,因为它非常复杂,但我没有,答案是它足够聪明。我会写一个完整的答案。
                // 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;
                    }
                }
    
                // 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);
                }
    
            }
            // Let `Drain::drop` move the tail back if necessary and restore `vec.len`.
        }
    }