我遇到了以下 Rust 片段(这里):
fn main() {
let foo = (0..100)
.skip(2)
.inspect(|x| { dbg!(x); })
.take(5)
.collect::<Vec<u32>>();
dbg!(foo);
}
乍一看,我认为
inspect
会根据范围(2..100)打印所有值。 此外,我假设 foo
中的最终结果为 [2, 3, 4, 5, 6]。
虽然后者是正确的,但检查也打印了相同的数字,丢失了 (7..100) 中的所有内容:
[src/main.rs:4:20] x = 2
[src/main.rs:4:20] x = 3
[src/main.rs:4:20] x = 4
[src/main.rs:4:20] x = 5
[src/main.rs:4:20] x = 6
[src/main.rs:8:5] foo = [
2,
3,
4,
5,
6,
]
我认为
inspect
的目的主要是为了调试和错误处理原因,如文档中所述,但我认为这种行为与这个想法相矛盾,不是吗?
此外,当更改代码以在创建迭代器后立即检查迭代器时,
inspect
的行为更加违反直觉 - 从 0 开始但以 7 结束:
fn main() {
let foo = (0..100).into_iter()
.inspect(|x| { dbg!(x); })
.skip(2)
.take(5)
.collect::<Vec<u32>>();
dbg!(foo);
}
[src/main.rs:3:20] x = 0
[src/main.rs:3:20] x = 1
[src/main.rs:3:20] x = 2
[src/main.rs:3:20] x = 3
[src/main.rs:3:20] x = 4
[src/main.rs:3:20] x = 5
[src/main.rs:3:20] x = 6
[src/main.rs:8:5] foo = [
2,
3,
4,
5,
6,
]
我知道 Rust 中的迭代器是惰性的,而且我听说这样的链也被编译器优化了,但是为什么在使用
inspect
时也是如此?
.take()
会消耗五个元素。.take()
。.skip()
和 .take()
之间发生的情况,我们将看到所需的五个元素(如果可以预先生成这么多元素)。
但是,
.skip()
必须消耗三个元素才能发出第一个元素,第一个元素将被take()
消耗;因此 .skip()
必须消耗七个元素。.skip()
之前发生的情况,我们将看到所需的七个元素(如果可以提前生成这么多元素)。
范围
(0..100)
可能会产生比后续阶段预期的七个元素更多的元素,但并非必须如此。