跳到主要内容

Rust 闭包

提示
  1. 闭包的基本概念:在 Rust 中,闭包是一种匿名函数,用于定义没有名称的函数,可以捕获其环境中的值。
  2. 定义和调用闭包:闭包使用 || 语法定义,可以无参数或带参数,通过赋值给变量调用,类似于函数调用。
  3. 闭包的高级用法:闭包可以包含多行代码,使用 {} 包裹,支持环境捕获模式,允许在闭包中使用、修改或移动其作用域内的变量。

在 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 中带参数的闭包的工作原理

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

在上述示例中,我们创建了一个闭包,它接受两个参数:xy。在闭包内部,我们将 xy 相加,并将结果赋值给 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. 在闭包内部不修改变量
  2. 在闭包内部修改变量
  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