我应该什么时候使用 `drain` 和 `into_iter`?

When should I use `drain` vs `into_iter`?

从表面上看,drain and into_iter 都提供了相似的迭代器,即遍历集合的值。然而,它们是不同的:

fn main() {
    let mut items1 = vec![0u8, 1, 2, 3, 4, 5, 6, 7, 8, 9];
    let items2 = items1.clone();

    println!("{:?}", items1.drain().count());
    println!("{:?}", items2.into_iter().count());

    println!("{:?}", items1);
    // println!("{:?}", items2); Moved
}

drain 取一个 &mut 到集合中,然后集合就可用了。 into_iter 消耗集合。每个迭代器的适当用途是什么?

它们之间有些多余。但是,正如您所说, Drain 只是借用了向量,特别是它具有与向量相关的生命周期。如果希望 return 一个迭代器,或者以最灵活的方式修改迭代器,使用 into_iter 更好,因为它没有链接到原始 Vec 的所有者。如果希望重用数据结构(例如重用分配),那么 drain 是最直接的方法。

此外,(某种程度上)理论上的担忧是 Drain 需要 才能使原始结构成为任何类型的有效实例,也就是说,要么保留不变量,要么在最后修复它们,而 IntoIter 可以随心所欲地破坏结构,因为它可以完全控制值。

我只说 "somewhat" 理论上的,因为在 std 中已经有一个小的真实世界的例子:HashMap 暴露了 .drain.into_iter通过它的内部 RawTable 类型,它也有这些方法。 into_iter只能read the hash of the value being moved directly and that's that, but drain has to be careful to update the hash to indicate that the cell is then empty,不能随便看。显然,在这种情况下这绝对是很小的(可能只有一两个额外的指令),但是对于像树这样更复杂的数据结构,打破数据结构的不变量可能会带来一些不平凡的收益。

使用后drain, the Vec is empty but the storage previously allocated for its elements remains allocated. This means that you can insert new elements in the Vec without having to allocate storage for them until you reach the Vec's capacity.

请注意,在您再次使用 Vec 之前,您必须删除对 Drain 迭代器的所有引用。 DrainDrop 的实现将删除尚未从 Vec 中删除的所有元素,因此即使您没有完成,Vec 也将为空迭代。

对于 2018 版 Rust:

into_iter 消耗集合本身,drain 仅消耗集合中的值。

因此 drain 只允许排出一部分集合,现在实际上需要指定一个范围。 (自从提出问题以来,这似乎已经发生了变化。)

因此,如果你想使用整个集合,请使用 into_iter,如果你只想使用部分集合,或者如果你想稍后重用清空的集合,请使用 drain