Rust进阶(4)-闭包

闭包

闭包

闭包是可以保存进变量或作为参数传递给其它函数的匿名函数。闭包和函数不同的是,闭包允许捕获调用者作用域中的值

闭包的基本使用方式

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
35
36
37
38
fn main() {
let use_closure = || {
println!("This is a closure!");
};
use_closure();

//闭包定义会为每个参数和返回值类型推导一个具体的类型,但是不能推导两次(就是说同一个闭包,只能传入同一种类型)
let add_one_v2 = |x: u32| -> u32 { x + 1 };
//推导(就是说同一个闭包,只能传入同一种类型)
let add_one_v3 = |x| { x + 1 };
//简单函数体可以直接简化实现
let add_one_v4 = |x| x + 1;
let a = add_one_v1(5);
let b = add_one_v2(5);
let c = add_one_v3(5);
let d = add_one_v4(5);
println!("a = {}, b = {}, c = {}, d = {}", a, b, c, d);

//不能推导两次的例子
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
print!("s = {}", s);
//前面传入的参数已经被推导为String类型,此时传入u32类型,执行会报错
let n = example_closure(5);
// let n = example_closure(5.to_string());
println!("n = {}", n);

//捕捉环境中的变量,也就是说可以捕获上下文当前环境的变量
let i = 1;
let exe = |x| x +i;
let r = exe(5);
println!("r = {}",r);
}

//传统标准函数方式
fn add_one_v1(x: u32) -> u32 {
x + 1
}

带有泛型和fn traid特征的闭包

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
35
36
37
38
39
//实现一个缓存,只处理第一次传入的值并保存
struct Cacher<T>
where T: Fn(u32) -> u32
{
calcuation: T,
value: Option<u32>,
}

impl<T> Cacher<T>
where T: Fn(u32) -> u32
{
fn new(calcuation: T) -> Cacher<T> {
Cacher {
calcuation,
value: None,
}
}

fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calcuation)(arg);
self.value = Some(v);
v
}
}
}
}

fn main() {
let mut c = Cacher::new(|x| x + 1);
let v1 = c.value(1);
println!("v1 = {}", v1);

//此时传入别的值,返回的依旧是上面的结果
let v2 = c.value(30);
println!("v2 = {}", v2);
}

捕获环境中的值

闭包可以通过三种功能方式捕获其环境,它们对应函数的三种获取参数的方式,分别是获取所有权、可变借用、不可变借用。
这三种捕获值的方式被编码为如下三个Fn trait:

  1. FnOnce消费从周围作用域捕获的变量,闭包周围的作用域被称为其环境。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移进闭包。其名称的Once部分代表了闭包不能多次获取相同变量的所有权。
  2. FnMut获取可变的借用值,所以可以改变其环境
  3. Fn从其环境获取不可变的借用值

当创建一个闭包时,rust会根据其如何使用环境中的变量来推断我们希望如何引用环境。由于所有闭包都可以被调用至少一次,因此所有闭包都实现了FnOnce。没有移动被捕获变量的所有权到闭包的闭包也实现了FnMut;而不需要对捕获的变量进行可变访问的闭包实现了Fn。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn main() {
//例子-1
/*let x = 4;
let equal_to_x = |z| z==x;
let y = 4;
assert!(equal_to_x(y));*/

//例子-2
let x = vec![1,2,3];
let equal_to_x = move |z| z==x; //x的所有权移入闭包
println!("x = {:?}",x); //x所有权已被转移
let y = vec![1,2,3];
assert!(equal_to_x(y));
}

总结

本文编辑完毕

参考

[1] Rust 程序设计语言

  • Copyrights © 2017-2023 Jason
  • Visitors: | Views:

谢谢打赏~

微信