Rust 闭包
- 闭包的基本概念:在 Rust 中,闭包是一种匿名函数,用于定义没有名称的函数,可以捕获其环境中的值。
- 定义和调用闭包:闭包使用
||
语法定义,可以无参数或带参数,通过赋值给变量调用,类似于函数调用。 - 闭包的高级用法:闭包可以包含多行代码,使用
{}
包裹,支持环境捕获模式,允许在闭包中使用、修改或移动其作用域内的变量。
在 Rust 中,闭包是没有名称的函数。它们也被称为匿名函数或 lambda 函数。
在 Rust 中定义闭包
以下是 我们在 Rust 中创建闭包的方式,
// 定义一个打印文本的闭包
let print_text = || println!("Defining Closure");
在上面的例子中,我们创建了一个打印文本“Defining Closure”的闭包。这里,
print_text
- 存储闭包的变量||
- 闭包的开始println!("Defining Closure")
- 闭包的主体
调用闭包
定义闭包后,我们需要像调用函数一样调用它。要调用闭包,我们使用分配了该闭包的变量名。例如,
// 定义一个打印文本的闭包
let print_text = || println!("Defining Closure");
// 调用闭包
print_text();
这里,print_text()
调用了闭包。
示例:Rust 中的闭包
fn main() {
// 闭包打印文本
let print_text = || println!("Hello, World!");
print_text();
}
输出
Hello, World!
在上面的例子中,我们定义了一个闭包并将其存储在 print_text
变量中。然后我们使用 print_text()
调用这个闭包。
带参数的 Rust 闭包
在 Rust 中,我们还可以向闭包传递参数。例如,
// 定义一个闭包来将整数加 1
let add_one = |x: i32| x + 1;
这里,
let add_one
- 是存储闭包的变量的名称|x: i32|
- 是我们传递给闭包的参数及其类型x + 1;
- 是闭包的主体,返回x + 1
如果我们创建了一个带参数的闭包,我们在调用闭包时也需要传递值。
// 使用值 2 调用闭包
add_one(2);
示例:带参数的 Rust 闭包
fn main() {
// 定义一个闭包并将其存储在变量中
let add_one = |x: i32| x + 1;
// 调用闭包并将结果存储在变量中
let result = add_one(2);
println!("Result = {}", result);
}
输出
Result = 3
在上面的示例中,我们定义了一个闭包并将其绑定到 add_one
变量。然后我们使用 add_one(2)
调用闭包,并将返回值绑定到 result
变量。
以下是程序的工作方式,
Rust 中的多行闭包
我们还可以在闭包内包含多个语句。在这种情况下,我们使用大括号 {}
将这些语句括起来。
让我们看一个例子。
fn main() {
// 定义一个多行闭包
let squared_sum = |x: i32, y: i32| {
// 计算两个参数的和
let mut sum: i32 = x + y;
// 计算和的平方值
let mut result: i32 = sum * sum;
return result;
};
// 调用闭包
let result = squared_sum(5, 3);
println!("Result = {}", result);
}
输出
Result = 64
在上述示例中,我们创建了一个闭包,它接受两个参数:x
和 y
。在闭包内部,我们将 x
和 y
相加,并将结果赋值给 sum
变量。
最后,我们计算了 sum 的平方,并返回了 result
。
这里,花括号 {}
之间的代码表示闭包的主体。
Rust 中的闭包环境捕获
闭包具有一个独特的特性,允许它捕获环境。这意味着闭包可以使用其作用域中的值。例如,
fn main() {
let num = 100;
// 一个捕获 num 变量的闭包
let print_num = || println!("Number = {}", num);
print_num();
}
输出
Number = 100
在这里,绑定到 print_num
的闭包使用了在其外部定义的变量 num
。这被称为闭包环境捕获。
Rust 中闭包环境捕获模式
闭包的环境捕获可以基于变量和闭包定义,分为 3 种不同的模式。
- 在闭包内部不修改变量
- 在闭包内部修改变量
- 在闭包内部移动变量
让我们来看看这些环境捕获的模式。
1. 在闭包内部不修改变量
fn main() {
let word = String::from("Hello");
// 不可变闭包
let print_str = || {
println!("word = {}", word);
};
// 在闭包外部可以进行不可变借用
println!("length of word = {}", word.len());
print_str();
}
输出
word = Hello
length of word = 5
在这里,变量 word
在闭包 print_str
内部没有被修改。由于变量默认是不可变的,我们可以在闭包内部对 word
进行任意次数的不可变引用。注意闭包变量 print_str
也是不可变的。
这种捕获模式也被称为 通过不可变借用捕获。
2. 在闭包内部修改变量
fn main() {
let mut word = String::from("Hello");
// 可变闭包
let mut print_str = || {
// 在这里改变了 word 的值
word.push_str(" World!");
println!("word = {}", word);
};
// 不能进行不可变借用,因为变量在闭包内部已作为可变借用
// println!("length of word = {}", word.len());
print_str();
// 可以进行不可变借用,因为闭包已经被使用过
println!("length of word = {}", word.len());
}
输出
word = Hello World!
length of word = 12
在这里,变量 word
在闭包 print_str
内部被修改,使用了 word.push_str("World!");
。因此,我们必须将变量 word
以及闭包变量 print_str
都设为可变的。这意味着除非闭包被使用,否则不能存在对变量 word
的其他引用。
这种捕获模式也被称为 通过可变借用捕获。 3. 变量在闭包内部被移动
fn main() {
let word = String::from("Hello");
// 不可变闭包
let print_str = || {
// 将 word 变量移动到一个新变量
let new_word = word;
println!("word = {}", new_word);
};
print_str();
// 不能进行不可变借用,因为 word 变量已在闭包内部移动
// println!("length of word = {}", word.len());
}
输出
word = Hello
在这里,我们将变量 word
移动到闭包内的一个新变量 new_word
。由于变量被移动,我们除了在闭包内部,无法在其他地方使用它。
这种捕获模式也被称为 通过移动捕获。
常见问题解答
Rust 中函数和闭包有什么区别?
Rust 中函数和闭包的主要区别在于,闭包可以捕获其作用域中的值(环境捕获),而函数则设计上不会这样做。
此外,我们在 Rust 中创建闭包和函数的方式也有一些不同。例如,
// 用于相加的函数
fn add_numbers(a: i32, b: i32) {
let sum = a + b;
println!("Sum: {}", sum);
}
// 用于相加的闭包
let add_numbers = |a: i32, b: i32| {
let sum = a + b;
println!("Sum: {}", sum);
};
如何在 Rust 中将闭包作为函数参数使用?
让我们看一个以闭包作为函数参数的例子。
// 接收闭包的函 数
fn add_one<F: Fn(i32) -> i32>(x: i32, f: F) -> i32 {
return f(x) + 1;
}
fn main() {
// 用于求平方的闭包
let square = |x: i32| { x * x };
// 使用闭包作为第二个参数调用 add_one 函数
let result = add_one(5, square);
println!("result = {}", result);
}
输出
result = 26
这里,函数 add_one
以闭包作为第二个参数。我们首先运行闭包返回整数的平方 |x: i32| { x * x }
,然后在 add_one
函数中将 1 加到闭包的结果上。
return f(x) + 1;
因此,当我们将 5 作为第一个参数和 square
闭包作为第二个参数传递时,结果是 26,即 5 * 5 + 1
。