这篇文章收录于Rust 实战专栏。这个专栏中的相关代码来自于我开发的笔记系统。它启动于是2023年的9月14日。相关技术栈目前包括:Rust,Javascript。关注我,我会通过这个项目的开发给大家带来相关实战技术的分享。
我们写的代码主要有两部分,第一部分是正常的业务流程,第二部分是错误处理。 通常情况下,这两部分代码不是分开的,而是杂糅在一起的。 这让我们后期维护正常的业务逻辑非常头疼。
今天分享的是Rust语言中的错误处理的魔法, 它可以帮助我们写代码时,将错误处理的“体积”压缩到最小,同时还保证错误分支能被一个剩的处理掉。
例子代码
pub async fn insert_or_update_note(note: &Note, client: &Client) -> Result<String, MyError> {
let id: String = if note.has_id() {
note.id.clone()
} else {
gen_uuid()
};
if note.has_id() {
let _ = client
.execute(
"update notes set ...",
&[...],
)
.await?;
} else {
let _ = client
.execute(
"insert into notes ...",
&[...],
)
.await?;
}
Ok(id)
}
上面的代码直接关注的是业务逻辑的表达,要说错误处理部分,除了"?",就没有别的了。这就是我所说的魔法。如何应用这个魔法,使其为代码的可维护性发挥最大作用呢?
蓝图
首先,为我们的项目定义一个统一的错误类型MyError
,实际上就是实现std::convert::From<T>
trait。我们当然没有必要为每一种外部crate的错误类型去实现一次From<T>
。
这里面的关键要素是derive_more::{From}
,它直接实现了外部错误类型向MyError
的转换。而在我们的业务代码中,借助"?"语法,通过Rust编译器,神不知鬼不觉地就完成了错误分支的处理。
如果后续的代码维护中又加入了新的错误类型,直接向MyError里面增加对应的枚举值就可以了。是不是很方便啊?
总结
我们充分利用了Rust语言的关注点分离的语法特性,借助derive_more::From
,实现了错误类型的魔法转换。这对代码的可维护性来说,是一个价值不小的优化。
最后,欢迎大家留言交流。关注我,后面会在Rust 实战专栏中给大家带来更多关于Rust开发实战的分享。