rust 为什么Iterator::take_while拥有迭代器的所有权?

2wnc66cl  于 2022-12-19  发布在  其他
关注(0)|答案(2)|浏览(160)

我发现Iterator::take_while拥有迭代器的所有权是很奇怪的,它似乎是一个有用的特性,能够获取满足某个函数的前 x 个元素,但仍然保留原始迭代器中可用的其余元素。
我知道这与take_while的惰性实现不兼容,但仍然感觉很有用,这是因为它被认为没有足够的用处而不能包含在标准库中,还是有其他我没有看到的问题?

3wabscal

3wabscal1#

为了效率起见,所有的迭代器适配器都是按值获取原始迭代器的,另外,获取原始迭代器的所有权避免了在不必要的时候处理生存期。
如果你想保留对原始迭代器的访问,你可以使用by_ref,这引入了一个间接层,但是当需要这个特性时,程序员会选择加入额外的工作:

fn main() {
    let v = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut i1 = v.iter();
    for z in i1.by_ref().take_while(|&&v| v < 4) {
        //     ^^^^^^^^^
        println!("Take While: {}", z);
    }

    for z in i1 {
        println!("Rest: {}", z);
    }
}

具有输出

Take While: 1
Take While: 2
Take While: 3
Rest: 5
Rest: 6
Rest: 7
Rest: 8

Iterator::by_ref之所以有效,是因为Iterator实现了对迭代器的任何可变引用:

impl<'_, I> Iterator for &'_ mut I
where
    I: Iterator + ?Sized,

这意味着你也可以接受一个可变的引用。括号的优先级是必需的:

for z in (&mut i1).take_while(|&&v| v < 4)

你注意到4丢失了吗?这是因为一旦take_while选择了一个值并决定不使用它,它就没有地方可以“放回”了。放回它将需要选择比总是需要的更多的存储和缓慢。
我使用itertools crate来处理类似的情况,特别是take_while_ref

use itertools::Itertools; // 0.9.0

fn main() {
    let v = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut i1 = v.iter();
    for z in i1.take_while_ref(|&&v| v < 4) {
        //     ^^^^^^^^^^^^^^^
        println!("Take While: {}", z);
    }

    for z in i1 {
        println!("Rest: {}", z);
    }
}
Take While: 1
Take While: 2
Take While: 3
Rest: 4
Rest: 5
Rest: 6
Rest: 7
Rest: 8
jv4diomz

jv4diomz2#

如果它变得太复杂,我们可能用错了工具。
请注意,此处存在4。

fn main() {
    let v = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut i1 = v.iter().peekable();
    while let Some(z) = i1.next_if(|&n| n < &4) {
        println!("Take While: {z}");
    }
    for z in i1 {
        println!("Rest: {z}");
    }
}
Take While: 1
Take While: 2
Take While: 3
Rest: 4
Rest: 5
Rest: 6
Rest: 7
Rest: 8

Playground
是的,OP要求take_whileShepmaster's solution是一流的。

相关问题