XingPiaoLiang's

Back

何为闭包?#

闭包是一种匿名函数,可以赋值给变量也可以作为参数传递给其他其他函数,不同于函数的是:它允许捕获调用者作用域中的值

GPT: 闭包和函数有什么不同?

函数是执行代码的基本单位,而闭包是在函数基础上,加上了它的外部变量环境,使函数在离开原作用域后依然能访问那些变量。

闭包中的类型推导#

函数,作为一个提供给用户使用的API,当然需要显式地声明传入参数类型。但闭包并不会作为API对外提供,因此可以享受编译器的类型推导能力

fn main() {
	let closure = |x| x;
	let s = closure(String::from("Hello"));
	let y = closure(3);
}
rust

虽然类型推导很好用,但是它不是泛型,当编译器推导出一种类型后,它就会一直使用该类型

结构体中的闭包#

// 实现一个缓存对象
struct Cacher<T>
where
	T: Fn(u32) -> u32, {
	query: T,
	value: Option<u32>,
}
rust

此处 Fn(u32) -> u32是一个特征,代表一个闭包特征,T的特征约束则是一个拥有u32类型的参数,同时返回一个u32类型的闭包形式

三种不同的Fn特征#

同函数相同,闭包也存在三种参数传入的方式。

  1. FnOnce 直接获取(move)捕获变量的所有权
  2. FnMut 可变借用捕获环境中值
fn main() {
	let mut str = String::from("ERASERNOOB");
	// 获得可变引用
	// let mut string_c = |str| str.push_str("Hello");
	let string_c = |str| str.push_str("Hello");
}
fn exec<'a, F: FnMut(&'a str)>(mut f: F) {
	f("Hello")
}
rust

在使用FnMut类型闭包捕获外界的可变借用,常常搭配mut修饰符,但是这二者是独立的

fn main() {
	 // 拿走不可变引用
	let str = String::from("Hello");
	let c_str = || Println!("{}", c_str);

	 // 拿走所有权
	let str = String::from("Hello");
	let c_str = move || Println("{}", c_str);
}
rust
  • 拿到不可变引用,此时是可以Copy的
  • 如果拿到的是所有权或者是可变引用,都是不能copy的(并没有实现Copy

GPT: Rust 闭包会自动推断捕获方式. Rust 会优先使用最轻量的捕获方式。 只读 → &T,修改 → &mut T,消耗(move)→ T

  1. Fn特征,使用不可变借用捕获环境中的值
fn main() {
	let str = String::from("Hello");
	let func = |s| Prinln!("{}, {}", str, s);
	exec(func);
}

// 不可变借用
fn exec<'a, F: Fn(String)>(f: F) {
	f("world".to_string())
}
rust

一个闭包的实现了那种三大Fn特征中的哪一个,取决于该闭包如何使用被捕获的变量,而不是取决于捕获到该变量的方法。

三种Fn特征的关系#

  • FnOnce 每一种Fn都默认实现该特征
  • 没有移出捕获变量的所有权的 实现 FnMut
  • 不需要对捕获变量进行该表的 实现 Fn

闭包作为函数返回值#

  • 就算是闭包的声明签名一样,其返回的类型还是不相同。
  • 因为Rust在编译时要求函数的参数和返回类型必须有固定大小的内存空间。所以需要使用特征对象Box。
fn factory(x) -> Box<dyn Fn(i32) -> i32> {
	let num = 5;
	if x > 1 {
		Box::new(move |x| x + num)
	} else {
		Box::new(|x| Println!("{}", num))
	}
}

fn main() {
}
rust
Rust--浅尝闭包(Closure)
https://astro-pure.js.org/blog/rust-%E6%B5%85%E5%B0%9D%E9%97%AD%E5%8C%85closure
Author erasernoob
Published at April 25, 2025
Comment seems to stuck. Try to refresh?✨