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

发布于:2025-04-18 ⋅ 阅读:(15) ⋅ 点赞:(0)

🧠 零基础也能懂的 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> 这个值未来才会得到

✅ 举个例子:

Some(5)
  .and_then(|x| Some(x + 1))
  .and_then(|y| Some(y * 2))

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


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

✅ 左单位律(Left Identity)

定义:

unit(x).bind(f) == f(x)

用人话说:

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

示例:

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)

定义:

m.bind(unit) == m

用人话说:

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

示例:

let x = Some("hi");
let result = x.and_then(|v| Some(v)); // 就是 unit(v)

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

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


✅ 结合律(Associativity)

定义:

m.bind(f).bind(g) == m.bind(|x| f(x).bind(g))

用人话说:

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

示例:

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 的

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 串业务逻辑

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 不是为了炫技,而是为了安全、优雅、高复用地处理流程和异常

如果你能理解这三句话:

  • 我可以从值开始(左单位律)
  • 我可以随时停下不处理(右单位律)
  • 我可以拆写也能合写(结合律)

网站公告

今日签到

点亮在社区的每一天
去签到