学习笔记十六——Rust Monad从头学

🧠 零基础也能懂的 Rust Monad:逐步拆解 + 三大定律通俗讲解 + 实战技巧


📣 第一部分:Monad 是什么?

Monad 是一种"包值 + 链操作 + 保持结构"的代码模式,用来处理带上下文的值,并方便连续处理。

✅ 用人话怎么说?

你可以把 Monad 想成"装了值的容器",它还带了一套通用的处理流程,能帮你做以下三件事:

  1. 包裹值 :比如用户输入 5,你包装成 Some(5),表示"有值"。
  2. 自动判断是否处理:值存在就处理,不存在就跳过。
  3. 统一结构,不出错 :你不管怎么处理,最后结构还保持不变(比如一直是 Option<T>)。

🧩 第二部分:Monad 三大组成要素

这三样东西是判断一个类型是不是 Monad 的"标准配件"。

要素 名称 用通俗话解释 Rust 中的样子
① 包装器 类型构造器 把值"装进盒子" Some(x)Ok(x)async { x }
② 起点函数 单位函数(unit) 把普通值变成最简单的 Monad 容器 Some(x)Ok(x)
③ 链接器 绑定函数(bind) 如果有值就继续调用下一个操作 .and_then(...)

这些特性让我们可以放心大胆地"串"代码逻辑。


🔍 第三部分:什么叫"上下文"和"结构保持不变"?

例子 上下文的含义
Option<T> 这个值可能为空(None)
Result<T,E> 这个操作可能失败
Future<T> 这个值未来才会得到

✅ 举个例子:

rust 复制代码
Some(5)
  .and_then(|x| Some(x + 1))
  .and_then(|y| Some(y * 2))

这里的每一步都保留了 Option 结构,不会突然变成裸值 i32。这就叫结构不变


🧪 第四部分:三大定律彻底通俗讲清楚!

✅ 左单位律(Left Identity)

定义:

text 复制代码
unit(x).bind(f) == f(x)

用人话说:

把值放进盒子再处理,和你直接处理这个值,没区别!

示例:

rust 复制代码
fn f(x: i32) -> Option<i32> {
    Some(x + 1)
}

let a = Some(5).and_then(f); // 左边:unit(x).bind(f)
let b = f(5);                // 右边:直接调用 f(x)

assert_eq!(a, b);            // 都是 Some(6)

口诀:"左边装进去再处理,和直接处理一样。"


✅ 右单位律(Right Identity)

定义:

text 复制代码
m.bind(unit) == m

用人话说:

如果你对值"啥也不干就原样放回去",等于什么都没做。

示例:

rust 复制代码
let x = Some("hi");
let result = x.and_then(|v| Some(v)); // 就是 unit(v)

assert_eq!(result, x); // 不变

口诀:"右边原样返回,啥也没改变。"


✅ 结合律(Associativity)

定义:

text 复制代码
m.bind(f).bind(g) == m.bind(|x| f(x).bind(g))

用人话说:

不管你是"先 f 后 g"还是"把 f 和 g 合起来一起处理",结果一样!

示例:

rust 复制代码
fn f(x: i32) -> Option<i32> { Some(x + 1) }
fn g(x: i32) -> Option<i32> { Some(x * 2) }

let m = Some(3);
let a = m.and_then(f).and_then(g);
let b = m.and_then(|x| f(x).and_then(g));

assert_eq!(a, b); // 都是 Some(8)

理解要点:

  • f(x) 是第一步
  • g(...) 是第二步
  • 两种写法是"逐步绑定"和"整体组合"的区别

口诀:"多步绑定能拆合,合成一起也不差。"


📘 第五部分:从例子理解 Option 是怎么应用 Monad 的

rust 复制代码
fn validate_email(email: Option<String>) -> Option<String> {
    email.and_then(|e| {
        if e.contains("@") {
            Some(e)
        } else {
            None
        }
    })
}

每行拆解:

  • Option<String>:这个邮箱可能存在也可能不存在
  • .and_then(...):如果有值就执行闭包,否则直接 None
  • |e| {...}:取出值 e 后判断是否含有 @
  • 满足条件返回 Some(e),否则返回 None

体现了什么?

  • ✅ 用 Some(email) 开始:unit(x)
  • ✅ 用 .and_then(...) 处理:bind
  • ✅ 最后返回的仍是 Option<String>:结构不变

🔧 第六部分:.map() vs .and_then() 有啥区别?

方法 用法说明 示例
.map() 对值做处理,结果仍在容器内 `Some(2).map(
.and_then() 处理后返回另一个容器(嵌套) `Some(2).and_then(

.map() 相当于你"只动里面的值",.and_then() 是你"根据值决定接下去是否继续"。


🔁 第七部分:组合多个操作 - 用 Monad 串业务逻辑

rust 复制代码
fn parse_id(s: &str) -> Option<i32> {
    s.parse().ok()
}

fn check_id(id: i32) -> Option<i32> {
    if id > 0 { Some(id) } else { None }
}

fn query_user(id: i32) -> Option<String> {
    Some(format!("用户{}", id))
}

let user = Some("42")
    .and_then(parse_id)
    .and_then(check_id)
    .and_then(query_user);

用人话解释:

如果字符串能成功转成数字、这个数字大于 0、还能找到用户,就返回用户名;否则中途停止。

这就是典型的:组合多个失败可能的操作


🧾 第八部分:总结表格

类型 类型构造器 unit函数 bind函数 上下文解释
Option Some(x) Some(x) and_then 可能没有值
Result Ok(x)/Err(e) Ok(x) and_then 成功/失败状态
Future async { x } async await / then 值尚未获得

✅ 结语

掌握 Monad 不是为了炫技,而是为了安全、优雅、高复用地处理流程和异常

如果你能理解这三句话:

  • 我可以从值开始(左单位律)
  • 我可以随时停下不处理(右单位律)
  • 我可以拆写也能合写(结合律)
相关推荐
DongLi011 小时前
rustlings 学习笔记 -- exercises/06_move_semantics
rust
ssshooter5 小时前
Tauri 踩坑 appLink 修改后闪退
前端·ios·rust
布列瑟农的星空6 小时前
前端都能看懂的rust入门教程(二)——函数和闭包
前端·后端·rust
tingshuo291719 小时前
S001 【模板】从前缀函数到KMP应用 字符串匹配 字符串周期
笔记
蚂蚁背大象1 天前
Rust 所有权系统是为了解决什么问题
后端·rust
布列瑟农的星空1 天前
前端都能看懂的rust入门教程(五)—— 所有权
rust
Java水解2 天前
Rust嵌入式开发实战——从ARM裸机编程到RTOS应用
后端·rust
Pomelo_刘金2 天前
Rust:所有权系统
rust
Ranger09292 天前
鸿蒙开发新范式:Gpui
rust·harmonyos
DongLi015 天前
rustlings 学习笔记 -- exercises/05_vecs
rust