2024 Rust现代实用教程 closures闭包
文章目录
- 一、闭包基础概念
- 1.如何使用闭包
- 二、闭包获取参数byreference与byvalue
- 1.获取外部参数
- 2.所有权转移move
- 三、闭包是怎么工作的
- 1.闭包在底层是怎么工作的?
- 2.FnOnce,FnMut,Fn特质
- 四、闭包类型FnOnce、FnMut和Fn做函数参数的实例
- 参考
一、闭包基础概念
闭包是一种可以捕获其环境中变量的匿名函数
闭包的语法相对简洁灵活,同时也具有强大的功能。闭包在 Rust 中被广泛用于函数式编程、并发编程以及简化代码等方面。
1.如何使用闭包
定义闭包的语法类似 (但更简单)
- 在II内定义参数
- 可选地指定参数/返回类型
- 在{}内定义闭包体
你可以将闭包分配给一个变量,然后使用该变量,就像它是一个函数名,来调用闭包
Example:
#[derive(Debug)]
struct User {
name: String,
score: u64,
}
// 老派写法
// sort_by_key
// 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(&mut users);
sort_score_closure(&mut users);
println!("{:?}", users);
}
编译及运行:
cargo run
Compiling ch29_closure v0.1.0 (/home/wangji/installer/rust/project/ch29_closure)
warning: field `name` is never read
--> src/main.rs:3:5
|
2 | struct User {
| ---- field in this struct
3 | name: String,
| ^^^^
|
= note: `User` has a derived impl for the trait `Debug`, but this is intentionally ignored during dead code analysis
= note: `#[warn(dead_code)]` on by default
warning: `ch29_closure` (bin "ch29_closure") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 12.22s
Running `target/debug/ch29_closure`
3
[User { name: "U3", score: 40 }, User { name: "U2", score: 80 }, User { name: "U4", score: 90 }, User { name: "U1", score: 100 }]
二、闭包获取参数byreference与byvalue
1.获取外部参数
由Rust编译器决定用那种方式获取外部参数
1.不可变引用 Fn
2.可变引用FnMut
3.转移所有权(Move)FnOnce
2.所有权转移move
Rust编译器判断capturesby value
- 比方说在闭包手动drop该参数
move关键字强制将所有权转移到闭包
Example:
fn main() {
// Fn不可变引用获取外部参数
let s1 = String::from("1111111111111111111");
let s2 = String::from("2222222222222222222");
let fn_func = |s| {
println!("{s1}");
println!("I am {s}");
println!("{s1}");
};
fn_func("yz".to_owned());
fn_func("原子".to_owned());
println!("{s1} {s2}");
// FnMut 可变引用获取外部参数
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!("{s1}");
};
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();
// println!("{s1}");
// 捕获的参数强制move
let s1 = String::from("1111");
let move_fn = move || {
println!("{s1}");
}; // Fn : FnMut : FnOnce
move_fn();
// println!("{s1}");
let s1 = String::from("1111");
std::thread::spawn(move || println!("d {s1}")); //move确保线程运行的时候,s1还在
}
编译及运行:
cargo run
Compiling ch29_func v0.1.0 (/home/wangji/installer/rust/project/ch29_func)
warning: variable `fn_Once_func` should have a snake case name
--> src/main.rs:30:9
|
30 | let fn_Once_func = || {
| ^^^^^^^^^^^^ help: convert the identifier to snake case (notice the capitalization): `fn_once_func`
|
= note: `#[warn(non_snake_case)]` on by default
warning: `ch29_func` (bin "ch29_func") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.35s
Running `target/debug/ch29_func`
1111111111111111111
I am yz
1111111111111111111
1111111111111111111
I am 原子
1111111111111111111
1111111111111111111 2222222222222222222
1111111111111111111😀
I am yz
1111111111111111111😀
1111111111111111111😀😀
I am 原子
1111111111111111111😀😀
1111111111111111111😀😀 2222222222222222222😀😀
1111
1111
三、闭包是怎么工作的
1.闭包在底层是怎么工作的?
1.Rust编译器将闭包放入一个结构体
2.结构体会声明一个cal丨function,而闭包就是函数,cal丨function会包含闭包的所有代码
3.结构体会生产一些属性去捕获闭包外的参数
4.结构体会实现一些特质
- FnOnce
- FnMut
- Fn
2.FnOnce,FnMut,Fn特质
Fn继承至FnMut,FnMut继承至FnOnce
Example:
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);
}
编译及运行:
cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/ch30_closure_trait`
x is: 5
16
Example:可变应用的例子
- 能写非mut的尽量写非mut的,因为mut和非mut的性能差距很大
fn apply_closure<F: FnMut(i32, i32) -> i32>(mut closure: F, x: i32, y: i32) -> i32 {
closure(x, y)
}
fn main() {
let mut x = 5;
let mut add_closure = |a, b| {
x += 1;
println!("x is: {}", x);
a + b + x
};
let result = apply_closure(add_closure, 5, 6);
println!("{}", result);
}
编译及运行
cargo run
Compiling ch30_closure_trait v0.1.0 (/home/wangji/installer/rust/project/ch30_closure_trait)
warning: variable does not need to be mutable
--> src/main.rs:7:9
|
7 | let mut add_closure = |a, b| {
| ----^^^^^^^^^^^
| |
| help: remove this `mut`
|
= note: `#[warn(unused_mut)]` on by default
warning: `ch30_closure_trait` (bin "ch30_closure_trait") generated 1 warning (run `cargo fix --bin "ch30_closure_trait"` to apply 1 suggestion)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.33s
Running `target/debug/ch30_closure_trait`
x is: 6
17
四、闭包类型FnOnce、FnMut和Fn做函数参数的实例
Example:
// 只有不可变引用能用
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)); //性能第2
// 可变引用
let s1 = String::from("11111");
closure_fn_mut(|| println!("{}", s1)); //性能最差
// println!("{}", s1);
let mut s2 = String::from("22222");
closure_fn_mut(|| {
s2.push_str("😀");
println!("{}", s2);
});
println!("{s2}");
println!("======================");
// 所有权转移
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}")); //move主动修改所有权,性能最好
// println!("{s3}")
}
编译及运行
cargo run
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/ch31_fn`
11111
11111
11111
11111
22222😀
22222😀😀
22222😀😀
======================
11111
22222😀
22222😀
ff
参考
- 2024 Rust现代实用教程