要了解这里发生的情况,了解 Rust 中的 for 循环如何工作会很有帮助。
基本上,for 循环是使用迭代器的简写,因此:
for item in some_value {
// ...
}
基本上是一个简写
let mut iterator = some_value.into_iter();
while let Some(item) = iterator.next() {
// ... body of for loop here
}
所以我们可以看到,无论我们使用 for 循环进行什么循环,Rust 都会调用into_iter
方法从IntoIterator
特质上。 IntoIterator 特征看起来(大约)像这样:
trait IntoIterator {
// ...
type IntoIter;
fn into_iter(self) -> Self::IntoIter;
}
So into_iter
takes self
按价值和回报Self::IntoIter
这是迭代器的类型。当 Rust 移动任何按值获取的参数时,事情.into_iter()
调用后(或 for 循环后)不再可用。这就是为什么你不能使用coll
在你的第一个代码片段中。
到目前为止一切都很好,但是如果我们像下面这样循环引用集合,为什么我们仍然可以使用集合呢?
for i in &collection {
// ...
}
// can still use collection here ...
原因是对于很多收藏C
, the IntoIterator
不仅为集合实现了特征,还为集合的共享引用实现了特征&C
并且此实现会产生共享项目。 (有时它也被实现为可变引用&mut C
它产生对项目的可变引用)。
现在回到这个例子Range
我们可以检查它是如何实现的IntoIterator
.
看着Range 的参考文档 https://doc.rust-lang.org/std/ops/struct.Range.html, Range
奇怪的是似乎没有实施IntoIterator
直接...但是如果我们检查一揽子实施 https://doc.rust-lang.org/std/ops/struct.Range.html#blanket-implementationsdoc.rust-lang.org 上的部分,我们可以看到每个迭代器都实现了IntoIterator
特征(简单地,只需返回自身):
impl<I> IntoIterator for I
where
I: Iterator
这有什么帮助?嗯,检查一下进一步向上 https://doc.rust-lang.org/std/ops/struct.Range.html#impl-Iterator(在特征实现下)我们看到Range
确实实施Iterator
:
impl<A> Iterator for Range<A>
where
A: Step,
因此Range
确实实施IntoIterator
通过间接Iterator
。然而,两者都没有实施Iterator
for &Range<A>
(这是不可能的)或IntoIterator
for &Range<A>
。因此,我们可以通过传递以下内容来使用 for 循环Range
按值,但不按引用。
Why can &Range
不执行Iterator
?迭代器需要跟踪“它在哪里”,这需要某种突变,但我们不能突变&Range
因为我们只有一个共享引用。所以这是行不通的。 (注意&mut Range
能够并且确实实施Iterator
- 稍后会详细介绍)。
技术上可以实现IntoIterator
for &Range
因为这可以产生一个新的迭代器。但这有可能与一揽子迭代器的实现发生冲突Range
会非常高,事情会更加混乱。此外,一个Range
最多是两个整数,复制它非常便宜,所以实现起来确实没有太大价值IntoIterator
for &Range
.
如果你还想使用集合,可以克隆它
for i in coll.clone() { /* ... */ }
// `coll` still available as the for loop used the clone
这就提出了另一个问题:如果我们可以克隆范围并且复制它(如上所述)很便宜,为什么 Range 不实现Copy
特征?然后.into_iter()
调用将复制范围coll
(而不是移动它)并且它在循环之后仍然可以使用。根据这个公关 https://github.com/rust-lang/rust/pull/21846#issuecomment-110526401Copy 特征实现实际上存在,但被删除,因为以下内容被认为是脚枪(帽子提示迈克尔·安德森 https://stackoverflow.com/users/221955/michael-anderson指出这一点):
let mut iter = 1..10;
for i in iter {
if i > 2 { break; }
}
// This doesn't work now, but if `Range` implemented copy,
// it would produce `[1,2,3,4,5,6,7,8,9]` instead of
// `[4,5,6,7,8,9]` as might have been expected
let v: Vec<_> = iter.collect();
另请注意&mut Range
确实实现了迭代器,所以你可以这样做
let mut iter = 1..10;
for i in &mut iter {
if i > 2 { break; }
}
// `[4,5,6,7,8,9]` as expected
let v: Vec<_> = iter.collect();
最后,为了完整起见,了解当我们循环遍历 Range 时实际调用了哪些方法可能会很有启发:
for item in 1..10 { /* ... */ }
被翻译成
let mut iter = 1..10.into_iter();
// ˆˆˆˆˆˆˆˆˆ--- which into_iter() is this?
while let Some(item) = iter.next() { /* ... */ }
我们可以使用限定方法语法来明确这一点:
let mut iter = std::iter::Iterator::into_iter(1..10);
// it's `Iterator`s method! ------^^^^^^^^^
while let Some(item) = iter.next() { /* ... */ }