(撸起袖子)来来来,老师给你们翻译翻译什么叫错误处理!现在Rust圈子里流行这么个玩法:
【错误处理の校园版】
想象你开了一家奶茶店(crate),现在要给每种奶茶(模块)写一份《可能翻车清单》(错误枚举)。比如珍珠奶茶可能"珍珠煮糊了"、"糖加多了",水果茶可能"水果不新鲜"、"冰块不够"。有些店更狠,直接写一本《本店所有翻车大全》(全局错误枚举)。
但这样很蠢对吧?比如你做水果茶的服务员明明不会煮珍珠,但《翻车清单》里还是列着"珍珠煮糊了"这一条。顾客(调用代码)得自己翻说明书(文档)才知道哪些错误根本不会发生——谁特么看说明书啊!(摔)
【痛点暴击】
Rust最牛逼的地方不就是用类型系统当保安,不让代码干坏事吗?结果现在大家集体摆烂,整出一堆《新华字典》那么厚的错误枚举。当然也能理解——要是给每个函数都单独写错误类型,还要搞类型转换,那得累死程序猿啊!所以现在99%的人都选择躺平用大杂烩错误类型...除了几个头铁的学霸还在坚持。
【叛逆方案】
其实错误就应该像微信消息一样,一条是一条!函数只是可能返回某些错误组合,而不是定义错误。最早这么玩的是"恐怖"库(快去围观!),虽然要写很多.map_err(OneOf::broaden)这种咒语,错误多的时候还得手动拼乐高,但人家设计是真的优雅啊...(远目)
【老师的私藏神器】
最近发现个叫error_set!的宏,简直是类型体操外挂!看好了:
rust
errorset! {
奶茶机故障 = { 机器冒烟(btleplug::Error) }; // 引用其他错误类型
找奶茶杯错误 = { 没吸管, 超时, 杯子失踪 } || 奶茶机故障 || 过滤奶茶错误;
过滤奶茶错误 = { 连接断开, 超时 } || 奶茶机故障;
// 自动实现错误类型转换
fn 做奶茶() -> Result<(), 找奶茶杯错误> {
找杯子()?; // 这里?自动转换错误类型
Ok(())
}
}
这个宏可以:
1. 用"||"符号组合错误类型,像拼积木一样
2. 自动生成转换逻辑,?操作符直接起飞
3. 甚至支持带字段的错误结构体
Rust代码:
error_set! { |
它允许我们从变量和与其他错误集的联合中创建错误集。如果你使用的错误集是函数错误集的子集,那么?操作符将起作用,即使你不使用union操作符,它也会发现是否是这种情况,即:
error_set! { |
虽然处理复杂错误时还是要写点模板代码,但比原来轻松多了!还有像例如SmartErr之类的库也在探索这个方向,最夸张的甚至有个#[属性宏]能自动分析函数体生成错误类型...(虽然老师现在找不到这个库了,知道的同学私我啊!)
(敲黑板)总之:错误处理应该像发朋友圈一样简单精准,而不是让所有人交同一份检讨书!