如何在Rust泛型函数中同时使用非所有权迭代器与消费型迭代器
Rust泛型迭代器调用的所有权问题解析
背景
在Rust中,我们可以对集合调用.iter()创建非所有权迭代器(返回集合元素的引用),也可以调用.into_iter()创建消费型迭代器(返回从集合中移出的值)。.iter()没有对应的 trait,但可以通过对集合的引用调用.into_iter()达到同样效果。
比如下面的函数可以正常编译:
fn test_vec(vec: Vec<i32>) { let i1 = (&vec).into_iter(); // 创建非所有权迭代器 let i2 = (&vec).into_iter(); // 再创建一个 let i3 = vec.into_iter(); // 创建消费型迭代器,会消耗集合 // 无法再创建非所有权迭代器 }
泛型化后的问题
想要将函数泛型化,使其支持任何实现IntoIterator<Item=i32>的集合,修改后的代码却无法编译:
fn test_generic<T: IntoIterator<Item = i32>>(vec: T) { let i1 = (&vec).into_iter(); // 创建非所有权迭代器 let i2 = (&vec).into_iter(); // 再创建一个 let i3 = vec.into_iter(); // 创建消费型迭代器,会消耗集合 // 无法再创建非所有权迭代器 }
编译报错
| let i1 = (&vec).into_iter(); // 创建非所有权迭代器 | ^^^^^^^----------- | | | | | 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
疑问
明明只是尝试复制&T类型的引用(不可变引用本身可以随意复制),为什么编译器要求T而非&T实现Copy trait?
原因分析
问题出在泛型约束的范围上:
- 原函数中
Vec<i32>的引用&Vec<i32>本身实现了IntoIterator<Item=&i32>,所以调用(&vec).into_iter()时,编译器会找到针对&Vec<i32>的IntoIterator实现——这个实现的into_iter方法接收的是self: &Vec<i32>(引用类型,本身是Copy的,因此可以多次调用)。 - 泛型版本中,我们只约束了
T: IntoIterator<Item=i32>,也就是**T自身**可以转换为产出i32的消费型迭代器,但并没有约束&T实现IntoIterator。此时编译器找不到&T对应的IntoIterator实现,就会尝试自动解引用&T得到T,然后调用T的into_iter方法——而这个方法需要获取T的所有权,导致vec被移动。因为T没有实现Copy,移动后就无法再次使用,因此触发报错。
解决方法
需要补充泛型约束,要求T的不可变引用也实现IntoIterator:
fn test_generic<T>(vec: T) where T: IntoIterator<Item = i32>, // 约束任意生命周期下的&T都实现IntoIterator for<'a> &'a T: IntoIterator, { let i1 = (&vec).into_iter(); let i2 = (&vec).into_iter(); let i3 = vec.into_iter(); }
如果需要明确引用迭代器的产出类型(比如&i32),可以将约束写得更具体:
fn test_generic<T>(vec: T) where T: IntoIterator<Item = i32>, for<'a> &'a T: IntoIterator<Item = &'a i32>, { let i1 = (&vec).into_iter(); let i2 = (&vec).into_iter(); let i3 = vec.into_iter(); }
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

