为什么 Rust 中的 Iterator::inspect 在应用 take 时表现得很奇怪?

问题描述 投票:0回答:1

我遇到了以下 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
时也是如此?

rust iterator
1个回答
0
投票

.take()
会消耗五个元素。
这意味着前一阶段必须发出这五个元素,仅此而已,以满足
.take()

检查
.skip()
.take()
之间发生的情况,我们将看到所需的五个元素(如果可以预先生成这么多元素)。

但是,

.skip()
必须消耗三个元素才能发出第一个元素,第一个元素将被
take()
消耗;因此
.skip()
必须消耗七个元素。
检查
.skip()
之前发生的情况,我们将看到所需的七个元素(如果可以提前生成这么多元素)。

范围

(0..100)
可能会产生比后续阶段预期的七个元素更多的元素,但并非必须如此。
这就是惰性迭代器的目的:仅在后续阶段实际需要时才生成后续阶段需要的内容。

© www.soinside.com 2019 - 2024. All rights reserved.