为何Rust迭代器的谓词使用FnMut而非Fn?
skip_while/take_while 用 FnMut 而不是 Fn? 这个问题问到了Rust闭包设计和API通用性的关键点,其实核心原因可以从三个角度来理解:
FnMut是Fn的超集,兼容更多实用场景
Rust的闭包trait有三个层级:FnOnce(只能调用一次)、FnMut(可以被可变借用并多次调用)、Fn(可以被不可变借用并多次调用)。其中Fn自动实现了FnMut——也就是说,所有符合Fn要求的闭包,都能无缝适配需要FnMut的场景。反过来,如果API约束成Fn,那那些需要修改捕获变量的闭包就没法用了。举个实际的例子:假设你想统计
skip_while跳过了多少元素,这时候需要在谓词里修改一个外部计数器:let mut skip_count = 0; let result = vec![1, 2, 3, 4, 5] .into_iter() .skip_while(|&num| { skip_count += 1; num < 3 }) .collect::<Vec<_>>(); // result 是 [3,4,5],skip_count 最终为2这个闭包捕获了可变的
skip_count,必须用FnMut才能编译通过。如果skip_while的约束是Fn,这种合理的使用场景就被禁止了,显然不太合理。谓词不修改参数,不代表闭包不能有内部状态
你提到“谓词仅返回布尔值、不修改值”,这里的“不修改值”指的是不修改传入的Self::Item(参数是&Self::Item,不可变引用),但闭包本身可以拥有自己的内部状态。FnMut的要求是闭包允许被可变借用,从而修改自己的捕获环境——这和是否修改参数完全是两回事。标准库的API没必要因为参数是不可变的,就限制闭包对自身状态的操作。API设计遵循“最小限制”原则
Rust标准库的API设计倾向于使用最通用的约束,只要能满足功能需求,就不会施加更严格的限制。用FnMut代替Fn,不会给只需要Fn的用户带来任何额外成本(因为Fn闭包自动兼容FnMut),但却能解锁更多灵活的使用场景,这是非常符合Rust设计哲学的选择。
内容的提问来源于stack exchange,提问作者Evan Carroll

