Rust 中的闭包
Rust 中的闭包(closure)是一类特殊的函数。与普通函数相比,闭包是匿名的(当然你可以把闭包赋值给一个变量);更重要的是,闭包可以从其被定义的作用域中捕获变量。
闭包的类型
在将闭包作为参数进行传递时,需要标注闭包的类型(实际上是 trait)。一共有三种类型:
-
Fn
,表示可以多次调用,但不修改捕获的变量(捕获为不可变引用); -
FnMut
,表示可以多次调用,且可能会修改捕获的变量(捕获为可变引用); -
FnOnce
,表示只保证单次调用的可用性。
move 关键字
在闭包前加上 move
关键字,会将闭包的捕获方式从默认的引用捕获变为值捕获。
需要注意的是,使用了
move
的闭包依然可以实现Fn
或FnMut
,这是因为闭包的类型取决于该闭包如何对捕获的变量进行操作,而不取决于它当初是如何捕获这些变量的。
fn main() {
let mut a = 0;
let mut fnmut = move || {
a += 1;
println!("Inner: {}", a);
};
fnmut();
println!("Outer: {}", a);
fnmut();
println!("Outer: {}", a);
}
在上面的例子中,最后的输出是:
Inner: 1
Outer: 0
Inner: 2
Outer: 0
也即闭包 fnmut
内部的 a
和主函数 main()
中的 a
是两个独立的变量。
需要注意的是,这里实际上隐式地进行了一次 copy
。如果捕获的量没有实现 Copy
,则会进行 move
,此时对应的量就不能再在外部环境中调用了。例如下面的例子:
fn main() {
let data = vec![1, 2, 3];
std::thread::spawn(move || {
println!("captured {:?} by value", data)
}).join().unwrap();
println!("{:?}", data);
}
就会报错
error[E0382]: borrow of moved value: `data`
--> src/main.rs:8:22
|
2 | let data = vec![1, 2, 3];
| ---- move occurs because `data` has type `Vec<i32>`, which does not implement the `Copy` trait
3 |
4 | std::thread::spawn(move || {
| ------- value moved into closure here
5 | println!("captured {:?} by value", data)
| ---- variable moved due to use in closure
...
8 | println!("{:?}", data);
| ^^^^ value borrowed here after move
|
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
网友评论