以下内容为本人学习过程中的记录笔记,其中可能存在不准确或错误,欢迎勘误及指正

创建闭包

闭包的创建和函数的创建基本一致,但不用使用fn来定函数。比如下面的main函数中,我们定义了一个闭包

1
2
3
4
5
6
7
8
9
fn main() {
// 标准语法
let add = |x: f64, y: f64| -> f64 {
println!("{} + {} = {}", x, y, x + y);
x + y
};

let _result = add(1.0, 2.0); // 调用闭包
}

一般情况下,我们其实可以采用简化语法来创建闭包

1
2
3
4
5
6
7
8
9
fn main() {
// 简化语法
let add = |x, y| {
println!("{} + {} = {}", x, y, x + y);
x + y
};

let _result = add(1.0, 2.0); // 调用闭包
}

闭包中的变量类型

在创建和使用闭包时,编译器通常可以直接推断出使用的变量类型而不用自己定义。但需注意的是,变量类型确定后就不能再使用其他类型的变量了

1
2
3
4
5
6
fn main() {
let c = |x| x;

let s = c(String::from("hello"));
let n = c(5); // 注意:该行会发生panic,因为闭包c的参数类型已经确定为"String"类型
}

捕获环境

闭包可捕获其所在定义域的环境值,这让闭包的使用比普通函数灵活得多

1
2
3
4
5
6
7
8
fn main() {
let var = 3;
let var_add = |x| {
println!("{} + {} = {}", x, var, x + var)
};

var_add(3);
}

类似的,如果使用函数的话就不能满足这样的要求了

1
2
3
4
5
6
7
8
9
fn main() {
let var = 3;
// 以下的写法会引发panic
fn var_add(x: i32) {
println!("{} + {} = {}", x, var, x + var)
}

var_add(3)
}

闭包所捕获环境值的所有权

闭包捕获的环境值的所有权和普通函数的所有权保持一致,一共有三种方式

取得变量所有权:FnOnce

满足FnOnce trait的闭包捕获的环境值只能被闭包调用一次,后续无法再次调用

1
2
3
4
5
6
7
8
9
fn main() {
let name = String::from("rust");
let c = move |greeting: String| (greeting, name); // 使用move关键字将变量name的所有权传入闭包中

let result = c("hello".to_string());
println!("result: {:?}", result);

println!("{}", name); // 因为变量name的所有权已经转移了,所以这行会发生错误
}

获得变量的可变借用:FnMut

满足FnMut trait的闭包可以获得环境值的可变借用

1
2
3
4
5
6
7
8
9
10
fn main() {
let mut name = String::from("hello");
let mut c_mut = || {
name.push_str(" rust");
println!("c: {}", name);
};
c_mut();

println!("{}", name); // 变量name的所有权没有改变,但其中的值已经被修改了
}

获得变量的不可变借用:Fn

满足Fn trait的闭包不会消耗所捕获环境值的所有权

1
2
3
4
5
6
7
8
fn main() {
let y = String::from("hello");
let closure = |x| {
x == y
};
println!("{}", closure("hello"));
println!("{}", y);
}

在创建闭包时,Rust可以通过闭包对环境值的使用来推断出闭包具体使用哪个trait,具体而言,即(1)所有的闭包都实现了FnOnce trait、(2)没有移动捕获变量的实现了FnMut trait、(3)无需可变访问捕获变量的闭包实现了Fn trait

闭包作为参数

Rust支持函数式编程,所以闭包也可以作为参数、返回值和结构体成员变量。但需注意值的类型是实现FnFnOnce或者FnMut trait的类型

作为传入参数

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
let closure = |x: i32| {
println!("print in closure: {}", x);
x
};

call_closure(closure)
}

fn call_closure(closure: impl Fn(i32) -> i32) {
closure(10);
}

作为返回值

1
2
3
4
5
6
7
8
9
10
11
fn main() {
let a = call_closure();
a(1);
}

fn call_closure() -> impl Fn(i32) -> i32 {
|x: i32| {
println!("print in closure: {}", x);
x
}
}

作为结构体成员变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
struct Cacher<T> 
where T: Fn(i32) -> i32,
{
calc: T,
value: Option<i32>,
}

impl<T> Cacher<T>
where T: Fn(i32) -> i32,
{
fn new(clac: T) -> Self {
Cacher {
calc: clac,
value: None,
}
}

fn value(&mut self, arg: i32) -> i32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calc)(arg); // 调用传入闭包
self.value = Some(v);
v
}
}
}
}

fn main() {
let mut cacher = Cacher::new(|x| x); // 将闭包传递进结构体中,作为成员变量

println!("caching: {:?}", cacher.value(2));
}