03-Rust 函数式编程特性(闭包 + Iterator + Option/Result + 链式调用)

摘要:本文系统总结 Rust 函数式编程核心特性,涵盖闭包(Closure)、Iterator 迭代器、Option/Result 函数式操作、链式调用与管道操作等核心内容。每个知识点配有完整代码示例、对比表格、实战场景及常见问题解答,帮助开发者掌握 Rust 的函数式编程范式。

关键词:Rust、函数式编程、闭包、Iterator、迭代器、Option、Result、链式调用、零成本抽象

适合人群:已掌握 Rust 基础的开发者、想学习函数式编程的程序员、想提高代码表达力的开发者

阅读时间:约 55 分钟

版本信息:Rust 1.70+ | 兼容 Windows/macOS/Linux


一、闭包(Closure)

1.1 闭包基础

  闭包是可以捕获其环境的匿名函数。闭包在 Rust 中广泛用于函数式编程。

rust 复制代码
let add = |x, y| x + y;
println!("{}", add(3, 5));  // 输出 8

let greet = |name: &str| format!("Hello, {}!", name);
println!("{}", greet("Rust"));  // 输出 "Hello, Rust!"

闭包语法说明

语法 说明 示例
` x, y x + y`
` x: i32, y: i32 -> i32 { x + y }`
` println!("Hello")`
` x { let y = x * 2; y + 1 }`

1.2 闭包捕获环境

  闭包可以捕获其作用域中的变量,有三种捕获方式:

捕获方式对比

捕获方式 说明 示例
不可变借用 &T,不修改变量 `
可变借用 &mut T,修改变量 `
移动所有权 move 关键字 `move
rust 复制代码
let count = 5;
let add_count = |x| x + count;  // 不可变借用
println!("{}", add_count(10));  // 输出 15

let mut count = 5;
let mut increment = || { count += 1; count };  // 可变借用
println!("{}", increment());  // 输出 6
println!("{}", increment());  // 输出 7

let count = 5;
let add_count = move |x| x + count;  // 移动所有权
// println!("{}", count);  // 错误!count 已被移动

1.3 Fn、FnMut、FnOnce

  Rust 为闭包提供了三种 trait,对应不同的捕获行为:

闭包 Trait 对比

Trait 捕获方式 调用次数 说明
Fn 不可变借用 多次 不修改环境
FnMut 可变借用 多次 修改环境
FnOnce 移动所有权 一次 消耗环境
rust 复制代码
fn apply<F>(f: F, x: i32) -> i32
where
    F: Fn(i32) -> i32,
{
    f(x)
}

let double = |x| x * 2;
println!("{}", apply(double, 5));  // 输出 10

闭包 Trait 选择指南

场景 推荐 Trait 原因
只读操作 Fn 不修改环境,可多次调用
状态修改 FnMut 需要修改内部状态
一次性操作 FnOnce 消耗捕获的变量
多线程传递 Fn + Send + Sync 线程安全

💡 开发建议 :优先使用 Fn,只有在需要修改状态时才使用 FnMutFnOnce。这样可以让闭包更灵活。


二、Iterator 迭代器

2.1 Iterator 基础

  Iterator 是 Rust 函数式编程的核心,提供了零成本抽象的迭代方式。

rust 复制代码
let v = vec![1, 2, 3, 4, 5];
let mut iter = v.iter();

assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));

迭代器类型对比

方法 返回类型 所有权 适用场景
.iter() &T 借用 只读访问
.iter_mut() &mut T 可变借用 修改元素
.into_iter() T 移动所有权 消耗集合

2.2 常用迭代器方法

转换方法

方法 说明 示例
.map() 转换每个元素 `.map(
.filter() 过滤元素 `.filter(
.flat_map() 展平嵌套迭代器 `.flat_map(
.enumerate() 添加索引 .enumerate()
.zip() 组合两个迭代器 .zip(other_iter)
rust 复制代码
let v = vec![1, 2, 3, 4, 5];

let doubled: Vec<i32> = v.iter().map(|x| x * 2).collect();
// [2, 4, 6, 8, 10]

let evens: Vec<i32> = v.iter().filter(|x| x % 2 == 0).copied().collect();
// [2, 4]

let result: Vec<i32> = v.iter()
    .filter(|x| x % 2 == 0)
    .map(|x| x * 3)
    .collect();
// [6, 12]

消费方法

方法 说明 返回值 示例
.collect() 收集到集合 Vec<T> .collect::<Vec<_>>()
.fold() 折叠计算 累加结果 `.fold(0,
.sum() 求和 总和 .sum()
.count() 计数 元素数量 .count()
.find() 查找第一个匹配 Option<T> `.find(
.any() 是否存在匹配 bool `.any(
.all() 是否全部匹配 bool `.all(

2.3 高级迭代器操作

链式操作

方法 说明 示例
.chain() 连接两个迭代器 .iter().chain(other.iter())
.cycle() 循环迭代 .iter().cycle().take(10)
.rev() 反向迭代 .iter().rev()
.skip() 跳过前 N 个 .iter().skip(2)
.take() 取前 N 个 .iter().take(3)
rust 复制代码
let v1 = vec![1, 2, 3];
let v2 = vec![4, 5, 6];

let combined: Vec<i32> = v1.iter().chain(v2.iter()).copied().collect();
// [1, 2, 3, 4, 5, 6]

let middle: Vec<i32> = (1..=10).skip(2).take(3).collect();
// [3, 4, 5]

let reversed: Vec<i32> = (1..=5).rev().collect();
// [5, 4, 3, 2, 1]

💡 开发建议:迭代器是惰性的(lazy),只有在调用消费方法时才会执行。这使得链式调用非常高效。


三、Option 与 Result 函数式操作

3.1 Option 函数式方法

Option 常用方法

方法 说明 示例
.map() 转换 Some 值 `.map(
.and_then() 链式 Option 操作 `.and_then(
.unwrap_or() 提供默认值 .unwrap_or(0)
.unwrap_or_else() 动态默认值 `.unwrap_or_else(
.ok_or() 转为 Result .ok_or("error")
.filter() 条件过滤 `.filter(
rust 复制代码
let some_num = Some(5);
let no_num: Option<i32> = None;

let doubled = some_num.map(|x| x * 2);  // Some(10)
let result = Some(5).and_then(|x| Some(x + 1));  // Some(6)
let value = no_num.unwrap_or(0);  // 0
let positive = Some(-5).filter(|x| *x > 0);  // None

3.2 Result 函数式方法

Result 常用方法

方法 说明 示例
.map() 转换 Ok 值 `.map(
.map_err() 转换 Err 值 `.map_err(
.and_then() 链式 Result 操作 `.and_then(
.unwrap_or() 提供默认值 .unwrap_or(0)
.or_else() 动态错误处理 `.or_else(
rust 复制代码
let ok_val: Result<i32, &str> = Ok(5);
let err_val: Result<i32, &str> = Err("error");

let doubled = ok_val.map(|x| x * 2);  // Ok(10)
let err_msg = err_val.map_err(|e| format!("Error: {}", e));
let result = Ok(5).and_then(|x| Ok(x + 1));  // Ok(6)
let value = err_val.unwrap_or(0);  // 0

3.3 组合操作

方法 说明 示例
.transpose() Option<Result<T, E>>Result<Option<T>, E> .transpose()
.flatten() 展平嵌套 Some(Some(5)).flatten()Some(5)
.inspect() 调试查看 `.inspect(

四、链式调用与管道操作

4.1 链式调用基础

  Rust 的链式调用让代码更简洁、更易读:

rust 复制代码
let v = vec!["apple", "banana", "cherry", "date", "elderberry"];

let result: Vec<String> = v.iter()
    .filter(|s| s.len() > 5)
    .map(|s| s.to_uppercase())
    .collect();

// ["BANANA", "CHERRY", "ELDERBERRY"]

链式调用优势

优势 说明 示例
可读性 从左到右阅读 .filter().map().collect()
简洁性 减少中间变量 无需临时变量
性能 惰性求值 只遍历一次
组合性 易于组合 可复用管道

4.2 性能优化

性能对比

方式 遍历次数 性能 说明
手动循环 1 次 需要手动优化
链式调用 1 次 编译器优化
多次遍历 N 次 不推荐

4.3 实战技巧

技巧 1:使用 copied() 处理引用

rust 复制代码
let v = vec![1, 2, 3];
let doubled: Vec<i32> = v.iter().copied().map(|x| x * 2).collect();

技巧 2:使用 flatten() 展平

rust 复制代码
let nested = vec![vec![1, 2], vec![3, 4], vec![5, 6]];
let flat: Vec<i32> = nested.into_iter().flatten().collect();
// [1, 2, 3, 4, 5, 6]

技巧 3:使用 partition() 分组

rust 复制代码
let v = vec![1, 2, 3, 4, 5, 6];
let (evens, odds): (Vec<i32>, Vec<i32>) = v.into_iter().partition(|x| x % 2 == 0);
// evens: [2, 4, 6], odds: [1, 3, 5]

💡 综合实战案例

实战 1:数据处理管道

rust 复制代码
#[derive(Debug)]
struct User {
    name: String,
    age: u32,
    email: Option<String>,
    active: bool,
}

fn process_users(users: Vec<User>) -> Vec<String> {
    users.into_iter()
        .filter(|u| u.active && u.age >= 18)
        .filter_map(|u| u.email.map(|e| (u.name, e)))
        .map(|(name, email)| format!("{} <{}>", name, email))
        .collect()
}

fn main() {
    let users = vec![
        User { name: "张三".to_string(), age: 25, email: Some("zhangsan@example.com".to_string()), active: true },
        User { name: "李四".to_string(), age: 17, email: Some("lisi@example.com".to_string()), active: true },
        User { name: "王五".to_string(), age: 30, email: None, active: true },
        User { name: "赵六".to_string(), age: 28, email: Some("zhaoliu@example.com".to_string()), active: false },
    ];

    let result = process_users(users);
    println!("{:?}", result);
    // ["张三 <zhangsan@example.com>"]
}

项目知识点

  • 迭代器链式调用(filterfilter_mapmap
  • Option 处理(filter_map 自动过滤 None)
  • 结构体数据处理
  • 函数式数据转换

实战 2:函数式配置解析器

rust 复制代码
use std::collections::HashMap;

#[derive(Debug)]
struct Config {
    settings: HashMap<String, String>,
}

impl Config {
    fn new(settings: HashMap<String, String>) -> Self {
        Self { settings }
    }

    fn get_int(&self, key: &str) -> Option<i32> {
        self.settings.get(key)
            .and_then(|s| s.parse::<i32>().ok())
    }

    fn get_bool(&self, key: &str) -> Option<bool> {
        self.settings.get(key)
            .map(|s| s.to_lowercase())
            .and_then(|s| match s.as_str() {
                "true" | "1" | "yes" => Some(true),
                "false" | "0" | "no" => Some(false),
                _ => None,
            })
    }

    fn get_or_default(&self, key: &str, default: &str) -> String {
        self.settings.get(key)
            .cloned()
            .unwrap_or_else(|| default.to_string())
    }
}

fn main() {
    let mut settings = HashMap::new();
    settings.insert("timeout".to_string(), "30".to_string());
    settings.insert("debug".to_string(), "true".to_string());
    settings.insert("name".to_string(), "MyApp".to_string());

    let config = Config::new(settings);

    println!("timeout: {:?}", config.get_int("timeout"));  // Some(30)
    println!("debug: {:?}", config.get_bool("debug"));  // Some(true)
    println!("name: {}", config.get_or_default("name", "DefaultApp"));  // MyApp
}

项目知识点

  • Option 链式操作(and_thenmapunwrap_or_else
  • 字符串解析(parse
  • HashMap 操作
  • 默认值处理

❓ 常见问题 FAQ

Q1:闭包和普通函数有什么区别?

  A:主要区别:

  1. 闭包是匿名函数,普通函数有名称
  2. 闭包可以捕获环境,普通函数不能
  3. 闭包类型由编译器推断,普通函数需要标注类型
  4. 闭包可以作为参数传递或返回值

Q2:Iterator 为什么是惰性的?

  A:惰性求值意味着迭代器只有在调用消费方法时才会执行。这样可以:

  1. 避免不必要的计算
  2. 支持无限序列
  3. 优化链式调用性能

Q3:.map().and_then() 有什么区别?

  A:区别:

  • .map() 转换值,返回 Option<T>Result<T>
  • .and_then() 转换值并展平,返回 Option<T>Result<T>
  • .and_then() 用于链式操作,避免嵌套

Q4:如何提高迭代器性能?

  A:优化技巧:

  1. 使用 .copied().cloned() 避免解引用
  2. 使用单次链式调用代替多次遍历
  3. 使用 .by_ref() 避免消耗迭代器
  4. 使用 .fold() 代替 .map().sum() 减少中间分配

Q5:什么时候使用 .unwrap() 什么时候使用 .expect()

  A:建议:

  • 开发阶段:使用 .expect("错误信息") 提供调试信息
  • 生产代码:使用 match? 运算符处理错误
  • 测试代码:可以使用 .unwrap() 快速失败

📝 学习资源与建议

学习建议

  1. 理解惰性求值 :迭代器只有在消费时才执行,这是函数式编程的核心

  2. 多练习链式调用 :从简单的 .map().filter() 开始

  3. 掌握 Option/Result :函数式错误处理是 Rust 的强项

  4. 阅读源码 :标准库迭代器实现是很好的学习材料

  5. 性能测试 :使用 cargo bench 验证链式调用性能

官方资源

练习平台


📚 参考资料

相关推荐
奈斯先生Vector1 小时前
AIGC 视频生成实战:用 Kling Video 拆解文生视频、图生视频与完整工作流
开发语言·人工智能·windows·python·aigc·音视频
吃饱了得干活1 小时前
Redis 不是死脑筋,它是一套“会进化”的存储系统
redis·后端
伩仁1 小时前
别再 HTTP 200 一把梭了:用 RFC 9457 Problem Details 给 FastAPI 错误响应"立规矩"
后端
小玮看世界1 小时前
[Python]螺旋遍历 vs 最短路径:方向控制类算法的“同源异流”
开发语言·python·算法
MeetTanG1 小时前
Go 实战锦囊|errgroup:优雅地管理并发任务组
后端·go
PragmaticWorks1 小时前
DDD 学了很多却用不上?因为你把“责任”和“时机”揉在了一起
后端·领域驱动设计
OPEN-F1 小时前
ROS2系列教程:tf2坐标变换详解(C++/Python)
开发语言·c++·python
凯哥Java1 小时前
System.setProperty 的正确姿势:Spring Boot 启动类里的“缺省值“魔法
java·spring boot·后端
XR1234567881 小时前
医院基础网络改造升级选型深度解析
开发语言·网络·php