Tailwind CSSTailwind CSS
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • 搜索未来:SEO与GEO双引擎实战手册
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • Rust 开发入门
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain
Home
  • Tailwind CSS 书籍目录
  • Vue 3 开发实战指南
  • React 和 Next.js 学习
  • TypeScript
  • React开发框架书籍大纲
  • Shadcn学习大纲
  • Swift 编程语言:从入门到进阶
  • SwiftUI 学习指南
  • 函数式编程大纲
  • Swift 异步编程语言
  • Swift 协议化编程
  • SwiftUI MVVM 开发模式
  • SwiftUI 图表开发书籍
  • SwiftData
  • ArkTS编程语言:从入门到精通
  • 仓颉编程语言:从入门到精通
  • 鸿蒙手机客户端开发实战
  • WPF书籍
  • C#开发书籍
learn
  • 搜索未来:SEO与GEO双引擎实战手册
  • Java编程语言
  • Kotlin 编程入门与实战
  • /python/outline.html
  • Rust 开发入门
  • AI Agent
  • MCP (Model Context Protocol) 应用指南
  • 深度学习
  • 深度学习
  • 强化学习: 理论与实践
  • 扩散模型书籍
  • Agentic AI for Everyone
langchain

8.1 闭包与高阶函数

在 Rust 中,闭包(Closure)是一种可以捕获其定义环境中的变量的匿名函数。它们与普通函数类似,但具有更大的灵活性。高阶函数(Higher-Order Function)则是指那些接受闭包作为参数,或者将闭包作为返回值的函数。这一特性使得 Rust 能够以优雅、简洁的方式实现函数式编程风格。

8.1.1 什么是闭包?

闭包本质上是一个可以存储在变量中,或作为参数传递给其他函数的代码块。与函数不同,闭包可以捕获其定义作用域中的变量。

基本语法:

let add_one = |x: i32| -> i32 { x + 1 };
let result = add_one(5);
println!("{}", result); // 输出: 6

闭包的语法由一对竖线 || 包围的参数列表,后跟一个表达式或代码块组成。参数类型和返回类型通常可以省略,由编译器推断。

简化形式:

// 单行表达式,可以省略花括号
let add_two = |x| x + 2;

// 多行代码块,需要花括号
let complex_closure = |x: i32| {
    let y = x * 2;
    y + 3
};

8.1.2 捕获环境变量

闭包的一个核心特性是能够捕获其定义所在作用域中的变量。这通过三种方式实现,与函数的借用规则类似:

  1. 不可变借用 (&T):闭包通过不可变引用来读取变量。
  2. 可变借用 (&mut T):闭包通过可变引用来修改变量。
  3. 所有权转移 (T):闭包取得变量的所有权。

编译器会根据闭包体内的操作自动推断捕获方式。

示例:不可变借用

let name = String::from("Rust");
let print_name = || println!("Hello, {}!", name); // 捕获 &name
print_name(); // 输出: Hello, Rust!
println!("{}", name); // 仍然可以访问 name

示例:可变借用

let mut count = 0;
let mut increment = || {
    count += 1; // 捕获 &mut count
};
increment();
increment();
println!("{}", count); // 输出: 2
// 注意:在可变借用闭包最后一次使用之前,不能有其他对 count 的借用

示例:所有权转移

let data = vec![1, 2, 3];
let take_ownership = || {
    let owned_data = data; // 捕获 data 的所有权
    println!("{:?}", owned_data);
};
take_ownership();
// println!("{:?}", data); // 错误!data 的所有权已被转移

8.1.3 move 关键字

有时我们需要强制闭包取得捕获变量的所有权,即使它只是进行借用操作。这在使用多线程时尤为重要,因为线程可能比闭包捕获的变量活得更久。

move 关键字放在闭包参数列表之前,指示编译器将所有捕获的变量所有权转移到闭包中。

let data = vec![1, 2, 3];
let thread_closure = move || {
    println!("Data from thread: {:?}", data);
};
std::thread::spawn(thread_closure).join().unwrap();
// 此时 data 的所有权已转移到闭包中,主线程无法再访问

8.1.4 闭包作为函数参数(高阶函数)

将闭包作为参数传递给函数是 Rust 中非常常见的模式。为了在函数签名中描述闭包的类型,我们使用 Fn、FnMut 和 FnOnce 这三个 trait。

  • FnOnce:表示闭包只能被调用一次。如果一个闭包捕获了变量的所有权,或者实现了 Drop trait,它通常实现 FnOnce。
  • FnMut:表示闭包可以多次被调用,并且可能会修改捕获的变量。
  • Fn:表示闭包可以多次被调用,并且不会修改捕获的变量(只进行不可变借用)。

示例:使用 Fn trait

fn apply_twice<F>(f: F, x: i32) -> i32
where
    F: Fn(i32) -> i32,
{
    f(f(x))
}

fn main() {
    let double = |x| x * 2;
    let result = apply_twice(double, 3);
    println!("{}", result); // 输出: 12 (3 * 2 * 2)
}

示例:使用 FnMut trait

fn call_mut<F>(mut f: F, x: i32)
where
    F: FnMut(i32) -> i32,
{
    let result = f(x);
    println!("{}", result);
}

fn main() {
    let mut acc = 0;
    let mut add_to_acc = |x| {
        acc += x;
        acc
    };
    call_mut(add_to_acc, 5); // 输出: 5
}

示例:使用 FnOnce trait

fn call_once<F>(f: F, x: i32)
where
    F: FnOnce(i32) -> i32,
{
    let result = f(x);
    println!("{}", result);
}

fn main() {
    let data = String::from("hello");
    let consume = |x| {
        println!("{}", data); // 捕获了 data 的所有权
        x + 1
    };
    call_once(consume, 10); // 输出: hello \n 11
}

8.1.5 闭包作为返回值

函数也可以返回闭包。由于闭包类型在编译时是未知的,我们需要使用 impl Trait 语法来返回一个实现了特定 trait 的闭包。

fn create_adder(x: i32) -> impl Fn(i32) -> i32 {
    move |y| x + y // 必须使用 move 关键字,因为 x 在函数返回后可能被销毁
}

fn main() {
    let add_five = create_adder(5);
    let result = add_five(10);
    println!("{}", result); // 输出: 15
}

8.1.6 常见的高阶函数

Rust 的标准库提供了大量使用闭包的高阶函数,尤其是在迭代器(Iterator)上。

  • map:对每个元素应用一个闭包,返回一个新的迭代器。
  • filter:根据闭包返回的布尔值过滤元素。
  • fold / reduce:将闭包应用于所有元素,累积成一个单一值。
  • for_each:对每个元素执行一个闭包,主要用于副作用操作。

示例:链式调用

let numbers = vec![1, 2, 3, 4, 5];

let sum_of_squares_of_evens: i32 = numbers
    .iter()
    .filter(|&&x| x % 2 == 0) // 过滤出偶数
    .map(|&x| x * x)          // 计算平方
    .sum();                    // 求和

println!("{}", sum_of_squares_of_evens); // 输出: 20 (4 + 16)

8.1.7 性能考量

闭包在 Rust 中是零开销抽象的。编译器会将闭包内联展开,生成与手写循环或显式函数调用几乎相同的机器码。因此,使用闭包不会带来运行时性能损失,同时还能提升代码的表达力和可读性。

总结

闭包和高阶函数是 Rust 语言中强大的工具。它们允许你以声明式的方式编写代码,将行为作为数据进行传递,从而构建出更灵活、更模块化的程序。理解 Fn、FnMut 和 FnOnce 这三个 trait 是掌握 Rust 闭包的关键,它们与 Rust 的所有权系统紧密结合,确保了内存安全。

Last Updated:: 5/9/26, 3:13 PM