You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Rust中结合scan与filter去除迭代器的外部可变状态?

问题

我尝试移除迭代器中的外部可变状态。原实现通过filter配合外部可变变量筛选递增最大值:

fn main() {
    let non_increasing: Vec<i32> = vec![1, 0, 2, 6, 3, 9];
    let mut max_so_far = 0;
    for val in non_increasing
        .iter()
        .filter(|&&height| if height > max_so_far {max_so_far = height; true} else {false})
        {
            println!("{val}")
        }
}

// output
// 1
// 2
// 6
// 9

我发现可用scan实现,但难以结合filter,于是采用了以i32::MIN为标记的临时方法(仅当该值不会出现时有效):

fn main() {
    let non_increasing: Vec<i32> = vec![1, 0, 2, 6, 3, 9];
    for val in non_increasing
        .iter()
        .scan(0, |max_so_far, &x|{
            if x > *max_so_far {
                *max_so_far = x;
                Some(x)
            } else {
                Some(i32::MIN)
            }
        })
        .filter(|&x| x != i32::MIN) 
        {
            println!("{val}")
        }
}

// output
// 1
// 2
// 6
// 9

不过我希望找到非hack的实现方式,类似如下自定义scan_filter的逻辑:

fn main() {
    let non_increasing: Vec<i32> = vec![1, 0, 2, 6, 3, 9];
    for val in non_increasing
        .iter()
        .scan_filter(0, |max_so_far, &x|{
            if x > *max_so_far {
                *max_so_far = x;
                (true, Some(x))
            } else {
                (false, Some(x))
            }
        })
        {
            println!("{val}")
        }
}

// output
// 1
// 2
// 6
// 9

请问该如何实现?

解决方案

你可以通过自定义迭代器适配器实现scan_filter,结合scan的状态管理和filter的筛选能力,完全避免标记值的hack写法。

1. 定义ScanFilter迭代器结构体

首先创建一个结构体封装迭代器、内部状态和处理闭包,然后为它实现Iterator特质:

use std::iter::Iterator;

struct ScanFilter<I, St, F> {
    iter: I,
    state: St,
    f: F,
}

impl<I, St, F, T> Iterator for ScanFilter<I, St, F>
where
    I: Iterator<Item = T>,
    F: FnMut(&mut St, T) -> (bool, Option<T>),
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.iter.next() {
                None => return None,
                Some(item) => {
                    let (keep, output) = (self.f)(&mut self.state, item);
                    if keep {
                        return output;
                    }
                }
            }
        }
    }
}

2. 添加迭代器扩展方法

为所有迭代器类型实现扩展特质,让scan_filter可以像标准库方法一样直接调用:

trait ScanFilterExt: Iterator {
    fn scan_filter<St, F>(self, initial_state: St, f: F) -> ScanFilter<Self, St, F>
    where
        F: FnMut(&mut St, Self::Item) -> (bool, Option<Self::Item>),
        Self: Sized,
    {
        ScanFilter {
            iter: self,
            state: initial_state,
            f,
        }
    }
}

// 为所有迭代器自动实现扩展特质
impl<I: Iterator> ScanFilterExt for I {}

3. 调用自定义的scan_filter

现在就可以按照你预期的逻辑使用,建议把初始值改为i32::MIN以兼容包含负数的数组:

fn main() {
    let non_increasing: Vec<i32> = vec![1, 0, 2, 6, 3, 9];
    for val in non_increasing
        .iter()
        .scan_filter(i32::MIN, |max_so_far, &x| {
            if x > *max_so_far {
                *max_so_far = x;
                (true, Some(x))
            } else {
                (false, None)
            }
        })
    {
        println!("{val}")
    }
}

输出结果和预期一致:

1
2
6
9

实现说明

  • 闭包通过返回(bool, Option<T>)直接控制元素是否保留以及输出值,无需依赖特殊标记值
  • 内部状态(比如max_so_far)可以在闭包中自由修改,完美结合状态管理和筛选逻辑
  • 扩展特质的实现让所有迭代器都能直接调用scan_filter,用法和标准库迭代器方法一致

内容的提问来源于stack exchange,提问作者Pioneer_11

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 02:30:59