本文属于我的 Rust 学习笔记 系列。
Rust 入门学习笔记以实际例子为主,讲解部分不是从零开始的,所以不建议纯萌新观看,读者最好拥有任意一种面向对象语言的基础,然后自己多多少少看过 Rust 的基本语法,刷过一点 rustlings。
来源:原子之音。当然也包含个人的一些补充。
视频
代码
Rust 进阶学习笔记以及实战的来源则五花八门,将会标注在下一行⬇️。
基础概念
闭包是一种可以捕获其环境中变量的匿名函数。
闭包的语法相对比较灵活,同时也具有强大的功能。闭包在 Rust 中被广泛用于函数式编程、并发编程和代码简化。
使用
定义
- 在
| |
内定义参数
- (可选)指定参数与返回类型
- 在
{ }
内定义闭包体
可以将闭包分配给一个变量,然后使用该变量,就像它是一个函数名一样。
例子
#[derive(Debug)]
struct User {
name: String,
score: u64,
}
fn sort_score(users: &mut Vec<User>) {
users.sort_by_key(sort_helper);
}
fn sort_helper(u: &User) -> u64 {
u.score
}
fn sort_score_closure(users: &mut Vec<User>) {
users.sort_by_key(|u| u.score); }
fn main() {
let f = |a, b| a + b;
println!("{}", f(1.0, 2.0));
let a = User {
name: "U1".to_owned(),
score: 100,
};
let b = User {
name: "U2".to_owned(),
score: 80,
};
let c = User {
name: "U3".to_owned(),
score: 40,
};
let d = User {
name: "U4".to_owned(),
score: 90,
};
let mut users = vec![a, b, c, d];
sort_score_closure(&mut users);
println!("{:?}", users);
}
闭包参数获取
闭包获取外部参数的方式共有三种:
- 不可变引用
Fn
- 可变引用
FnMut
- 转移所有权
FnOnce
默认情况下是由 Rust 编译器决定用哪种方式获取外部参数。
闭包是有层级的:Fn
能被当作FnMut
和FnOnce
使用;FnMut
能被当作FnOnce
;FnOnce
只能当作自己。也就是说,编译器会优先使用最不严格的特质进行推断。
所有权转移
如果想要进行 Move 操作,需要使用move
关键字强制将所有权转移到闭包。
另一种方式是在闭包中主动调用类似 drop 参数的方法将值消耗掉,此时能够自动推断。
例子
fn main() {
let s1 = String::from("1111111111111111111");
let s2 = String::from("2222222222222222222");
let fn_func = |s| { println!("{s1}");
println!("I am {s}");
println!("{s2}");
};
fn_func("yz".to_owned());
fn_func("原子".to_owned());
println!("{s1} {s2}");
let mut s1 = String::from("1111111111111111111");
let mut s2 = String::from("2222222222222222222");
let mut fn_func = |s| { s1.push_str("😀");
s2.push_str("😀");
println!("{s1}");
println!("I am {s}");
println!("{s2}");
};
fn_func("yz".to_owned());
fn_func("原子".to_owned());
println!("{s1} {s2}");
let s1 = String::from("1111");
let fn_once_func = || { println!("{s1}");
std::mem::drop(s1); };
fn_once_func(); let s1 = String::from("1111");
let move_fn = move || { println!("{s1}");
};
move_fn();
move_fn();
let s1 = String::from("1111");
std::thread::spawn(move || println!("d {s1}"));
}
工作原理
- Rust 会将闭包放入一个匿名结构体
- 结构体会声明一个 call 函数,由于闭包本身也是函数,所以该函数可以包含闭包的所有代码
- 结构体会生产一些属性去捕获闭包外的参数
- 结构体会根据上下文实现一些特质(
Fn
FnMut
FnOnce
)
例子
fn apply_closure<F: Fn(i32, i32) -> i32>(closure: F, x: i32, y: i32) -> i32 {
closure(x, y)
}
fn main() {
let x = 5;
let add_closure = |a, b| {
println!("x is: {}", x);
a + b + x
};
let result = apply_closure(add_closure, 5, 6);
println!("{}", result);
}
闭包特质和函数参数
fn closure_fn<F>(func: F)
where
F: Fn(),
{
func();
func();
}
fn closure_fn_mut<F>(mut func: F)
where
F: FnMut(),
{
func();
func();
}
fn closure_fn_once<F>(func: F)
where
F: FnOnce(),
{
func();
}
fn main() {
let s1 = String::from("11111");
closure_fn(|| println!("{}", s1));
let s1 = String::from("11111");
closure_fn_mut(|| println!("{}", s1));
let mut s2 = String::from("22222");
closure_fn_mut(|| {
s2.push_str("😀");
println!("{}", s2);
});
println!("{s2}");
let s1 = String::from("11111");
closure_fn_once(|| println!("{}", s1));
let mut s2 = String::from("22222");
closure_fn_once(|| {
s2.push_str("😀");
println!("{}", s2);
});
println!("{s2}");
let s3 = " ff".to_owned();
closure_fn_once(move || println!("{s3}"));
}