编程Rust开发者

Rust中的闭包是如何工作的?存在什么类型的闭包,它们之间有什么区别,以及何时使用?

用 Hintsage AI 助手通过面试

答案

在Rust中,闭包是可以从外部作用域捕获变量的匿名函数。语法如下:

let add = |a: i32, b: i32| a + b;

Rust中有三种类型的闭包(根据捕获变量的方式不同):

  • Fn: 以引用的方式捕获 (&T),可以多次调用而不修改环境。
  • FnMut: 以可变引用的方式捕获 (&mut T),在调用时可以修改闭包内的数据。
  • FnOnce: 只能被调用一次,因为它会获取捕获的数据的所有权 (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整个集合,不自觉地转移所有权,导致外部代码在第一次迭代后失去对数据的访问,导致尝试访问已转移的值。