如何在Rust中的泛型函数中同时使用非拥有迭代器和消耗迭代器



在Rust中,我们可以在各种集合上使用.iter()来创建一个非拥有迭代器,该迭代器返回对Vec等集合的引用。我们还可以使用.into_iter()创建一个消耗迭代器,然后返回从集合中移出的值。.iter()没有像.into_iter()那样的特性,但我们可以通过对集合的引用调用.into_iter()来实现同样的目的。

例如,此函数编译良好:

fn test_vec(vec: Vec<i32>) {
let i1 = (&vec).into_iter(); // create a non-owning iterator
let i2 = (&vec).into_iter(); // create another one
let i3 = vec.into_iter(); // create an owning iterator which consumes the collection
// no more non-owning iterators can be created
}

我想使这个函数通用。我希望它不仅接受i32的Vec,还接受碰巧实现IntoIdeator<项目=i32>。

这样做似乎很简单,但下面的泛型函数不再编译。

fn test_generic<T: IntoIterator<Item = i32>>(vec: T) {
let i1 = (&vec).into_iter(); // create a non-owning iterator
let i2 = (&vec).into_iter(); // create another one
let i3 = vec.into_iter(); // create an owning iterator which consumes the collection
// no more non-owning iterators can be created
}

编译失败,出现以下错误:

|     let i1 = (&vec).into_iter(); // create a non-owning iterator
|              ^^^^^^^-----------
|              |      |
|              |      value moved due to this method call
|              move occurs because value has type `T`, which does not implement the `Copy` trait
|
note: this function takes ownership of the receiver `self`, which moves value

我不太理解错误的这一部分:

move occurs because value has type `T`, which does not implement the `Copy` 

我没有试图复制T类型的值。我试图复制&T,即对T的引用,而不是T本身。我认为您可以毫无问题地复制不可变的引用。为什么T需要它,而不是&T、 实现Copy?

在泛型函数的上下文中,该类型唯一存在的东西来自边界。如果指定T: IntoIterator<Item = i32>,则只有T实现IntoIterator&T不实现。当然,autoderef会起作用,取消引用引用,但试图将值移出引用

如果要指定&T实现IntoIterator,方法如下:

fn test_generic<T>(vec: T)
where
T: IntoIterator<Item = i32>,
for<'a> &'a T: IntoIterator<Item = &'a i32>,
{

您编写的约束条件是vecIntoIterator。这包括集合(如Vec),它们具有.iter()方法来获得非消耗迭代器,但任何其他迭代器。如果vec是一个数据被消耗,然后立即被丢弃到空中的流,那么您的签名是完全匹配的(这是i32上的迭代器),但对流进行引用并不能保证您可以对i32上的引用进行迭代:无论您对接收到的整数做了什么,都会调用流并丢失"上一个"值。

有几种解决方案:

  • 如果你绝对想保持签名不变,你可以建立一个缓存。你可能对此并不明智(将整个迭代器收集到Vec或类似的文件中,然后对已知的数据结构做任何你想做的事情,或者在调用迭代器的地方做一些更精细的事情,并根据你的需要逐步构建缓存)
  • 相反,如果你知道你的所有输入都将是集合(即一堆元素,都可以随机访问,但不在乎它是Vec、切片、数组还是其他什么),你需要在签名中表达这一点:
fn foo<V>(v: V)
where for<'a> &'a V: IntoIterator<Item=&'a i32>,
V: IntoIterator<Item=i32>
{
let itr1 = (&v).into_iter();
let itr2 = (&v).into_iter();
for xi in itr1 {
println!("{}", xi);
}
for yi in itr2 {
println!("{}", yi);
}
for zi in v {
println!("{}", zi);
}
}
fn main() {
foo(vec![1,2,3]);
foo([1,2,3]);
}

最新更新