在Rust中,闭包是可以从外部作用域捕获变量的匿名函数。语法如下:
let add = |a: i32, b: i32| a + b;
Rust中有三种类型的闭包(根据捕获变量的方式不同):
&T),可以多次调用而不修改环境。&mut T),在调用时可以修改闭包内的数据。T)。Rust会自动确定所需的类型,但您也可以明确指定。
示例区别:
let s = String::from("hello"); // FnOnce let consume = move || println!("{}", s); // s在调用后不再可用
为什么带有关键字 move 的闭包不一定是 FnOnce,而可能是 Fn 或 FnMut?
答案:关键字 move 将变量的捕获以所有权的方式转移,但如果闭包内数据不被修改和销毁,闭包仍然可以与 Fn/FnMut 兼容。例如:
let s = String::from("abc"); let c = move || println!("String: {}", s); // c: impl Fn() c();
c 可以多次调用:值 s 被复制到闭包中,但并未销毁。
故事
一位初学Rust的开发者在一个数据流项目中尝试编写没有move的闭包,并将其传递到其他线程。结果编译器报错,因为出现了超出借用(outlived borrow),不得不紧急研究生命周期和移动的细节。
故事
在闭包内引入可变性时,没有将类型更改为 FnMut,导致在所有使用迭代器的地方出现了 "mismatched types" 的错误。
故事
在处理大型数组时,闭包通过move整个集合,不自觉地转移所有权,导致外部代码在第一次迭代后失去对数据的访问,导致尝试访问已转移的值。