gpt4 book ai didi

rust - 为什么借用的范围不是迭代器,但范围是?

转载 作者:行者123 更新时间:2023-12-02 04:35:21 30 4
gpt4 key购买 nike

范围如何被消耗的一个例子是:

let coll = 1..10;
for i in coll {
println!("i is {}", &i);
}
println!("coll length is {}", coll.len());
这将失败
error[E0382]: borrow of moved value: `coll`
--> src/main.rs:6:35
|
2 | let coll = 1..10;
| ---- move occurs because `coll` has type `std::ops::Range<i32>`, which does not implement the `Copy` trait
3 | for i in coll {
| ----
| |
| `coll` moved due to this implicit call to `.into_iter()`
| help: consider borrowing to avoid moving into the for loop: `&coll`
...
6 | println!("coll length is {}", coll.len());
| ^^^^ value borrowed here after move
|
note: this function consumes the receiver `self` by taking ownership of it, which moves `coll`
解决此问题的常用方法是借用 coll ,但这在这里不起作用:
error[E0277]: `&std::ops::Range<{integer}>` is not an iterator
--> src/main.rs:3:14
|
3 | for i in &coll {
| -^^^^
| |
| `&std::ops::Range<{integer}>` is not an iterator
| help: consider removing the leading `&`-reference
|
= help: the trait `std::iter::Iterator` is not implemented for `&std::ops::Range<{integer}>`
= note: required by `std::iter::IntoIterator::into_iter`
这是为什么?为什么借用的范围不是迭代器,但范围是?是不是有不同的解释?

最佳答案

要了解这里发生的事情,了解 for 循环在 Rust 中的工作原理会很有帮助。
基本上,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;
}
所以 into_iter需要 self按值返回 Self::IntoIter这是迭代器的类型。随着 Rust 移动任何按值获取的参数,事物 .into_iter()在调用之后(或在 for 循环之后)被调用不再可用。这就是为什么你不能使用 coll在你的第一个代码片段中。
到目前为止一切顺利,但是如果我们像下面这样循环引用它,为什么我们仍然可以使用它呢?
for i in &collection {
// ...
}
// can still use collection here ...
原因是对于很多收藏 C , IntoIterator trait 不仅为集合实现,还为集合的共享引用实现 &C这个实现产生共享项目。 (有时它也为可变引用实现 &mut C,它产生对项目的可变引用)。
现在回到 Range 的例子我们可以检查它是如何实现的 IntoIterator .
看着 the reference docs for Range , Range奇怪的是似乎没有实现 IntoIterator直接...但如果我们检查 Blanket Implementations在 doc.rust-lang.org 部分,我们可以看到每个迭代器都实现了 IntoIterator trait(简单来说,只是返回自身):
impl<I> IntoIterator for I
where
I: Iterator
这有什么帮助?嗯,检查 further up (在 trait 实现下)我们看到 Range确实实现 Iterator :
impl<A> Iterator for Range<A>
where
A: Step,
因此 Range确实实现 IntoIterator通过 Iterator 的间接.但是,没有实现 Iterator&Range<A> (这是不可能的)或 IntoIterator&Range<A> .因此,我们可以通过传递 Range 来使用 for 循环。按值,而不是按引用。
为什么可以 &Range未实现 Iterator ?迭代器需要跟踪“它在哪里”,这需要某种变异,但我们不能变异 &Range因为我们只有一个共享引用。所以这是行不通的。 (请注意 &mut Range 可以并且确实实现了 Iterator - 稍后会详细介绍)。
在技​​术上可以实现 IntoIterator&Range因为这可能会产生一个新的迭代器。但这可能会与 Range 的全面迭代器实现发生冲突。会非常高,事情会更加困惑。此外,还有一个 Range最多是两个整数,复制这个很便宜,所以实现 IntoIterator 真的没有什么大的值(value)。为 &Range .
如果你还想使用集合,你可以克隆它
for i in coll.clone() { /* ... */ }
// `coll` still available as the for loop used the clone
这带来了另一个问题:如果我们可以克隆范围并且复制它(如上所述)便宜,那么为什么 Range 不实现 Copy特征?然后是 .into_iter()调用将复制范围 coll (而不是移动它)并且它仍然可以在循环后使用。 According to this PR Copy trait 实现实际上存在,但被删除了,因为以下内容被认为是一个footgun(提示 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() { /* ... */ }

关于rust - 为什么借用的范围不是迭代器,但范围是?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63685464/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com