Rust 用 enum 与模式匹配实现类型安全的状态机:把非法状态变成编译错误
test12026-10-041 次阅读
为什么用 enum 建模状态
订单、设备、连接这些对象都有明确的生命周期。用字符串或整数记状态,编译期管不住非法跳转,bug 往往到运行时才暴露。Rust 的 enum 把「有哪些状态」变成类型,转移函数只能返回合法目标,非法状态在编译期就被拦下。
定义状态与事件
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OrderState {
Created,
Paid,
Shipped,
Completed,
Cancelled,
}
#[derive(Debug, Clone, Copy)]
enum OrderEvent {
Pay,
Ship,
ConfirmReceived,
Cancel,
}
把转移写成函数
转移函数接收当前状态与事件,返回 Result,只有合法组合才成功:
fn transition(state: OrderState, event: OrderEvent) -> Result {
use OrderEvent::*;
use OrderState::*;
let next = match (state, event) {
(Created, Pay) => Paid,
(Paid, Ship) => Shipped,
(Shipped, ConfirmReceived) => Completed,
(Created, Cancel) | (Paid, Cancel) => Cancelled,
_ => return Err(format!("非法转移: {:?} + {:?}", state, event)),
};
Ok(next)
}
在业务里消费
match 必须穷尽,漏掉某个状态编译器直接报错,等于一份永远不过期的状态文档:
fn describe(s: OrderState) -> &'static str {
match s {
OrderState::Created => "待支付",
OrderState::Paid => "已支付",
OrderState::Shipped => "已发货",
OrderState::Completed => "已完成",
OrderState::Cancelled => "已取消",
}
}
进阶:用关联数据承载上下文
状态可以携带数据,比如已发货要记下物流单号,类型系统在编译期保证你取得到:
enum Shipment {
Pending,
InTransit { tracking_no: String },
Delivered { signed_by: String },
}
fn tracking_no(s: &Shipment) -> Option<&str> {
match s {
Shipment::InTransit { tracking_no } => Some(tracking_no),
Shipment::Delivered { .. } => Some("已签收"),
Shipment::Pending => None,
}
}
工程收益
- 非法状态成为编译错误,而不是线上事故。
- 状态流转集中在一处 match,改动时编译器帮你找出所有遗漏分支。
- 状态与数据绑定,取值前不必做类型断言或空值判断。
T
test1
文章作者
为什么用 enum 建模状态 订单、设备、连接这些对象都有明确的生命周期。用字符串或整数记状态,编译期管不住非法跳转,...
- 分类
- 技术
- 发布时间
- 2026-10-04
- 字数
- 约 1596 字
- 阅读
- 1 次
