使用匹配(例如在bar
中)似乎是一种常见的方法。
#[derive(Debug)]
pub enum MyErrors {
SomeError,
}
fn foo(x: Option<u64>) -> Result<u64, MyErrors> {
if x.is_none() {
return Err(MyErrors::SomeError);
}
// .. some long code where more options
// are checked and matched
// The ok here is just so the code is simple and compiles
Ok(x.unwrap() * 2)
}
fn bar(x: Option<u64>) -> Result<u64, MyErrors> {
match x {
None => {
return Err(MyErrors::SomeError)?;
}
Some(v) => {
// .. some long code where more options
// are checked and matched
// The ok here is just so the code is simple and compiles
Ok(x.unwrap() * 2)
}
}
}
fn main() {
foo(Some(1));
bar(Some(2));
}
但是,早期返回(例如foo
中的返回)显着减少了嵌套代码的外观。如果多次必须解开选项或返回错误,则bar之类的代码会非常嵌套...
在空选项情况下尽早返回错误的建议做法是什么?
答案 0 :(得分:6)
如果由于内部复杂的逻辑而不希望使用更长的方法链,那么仍然有一些可读性低的缩进选项。
ok_or
和?
我们可以将Option
转换为具有所需错误的Result
,然后立即使用?
运算符对其进行拆包。此解决方案可能提供最小的缩进,并且可以轻松地用于“解包”多个Option
。
fn bar1(x: Option<u64>) -> Result<u64, MyErrors> {
let x = x.ok_or(MyErrors::SomeError)?;
// A lot of stuff going on.
Ok(x * 2)
}
这将评估ok_or
中的错误,无论是否实际使用该错误。如果此计算昂贵,则ok_or_else
(会延迟产生错误)将更有效(related question)。
if let
如果嵌套,此解决方案仍然会导致代码阶梯,但是如果更多地使用else
分支逻辑,则可能更合适。
fn bar2(x: Option<u64>) -> Result<u64, MyErrors> {
if let Some(x) = x {
// Lot of stuff here as well.
Ok(x * 2)
} else {
Err(MyErrors::SomeError)
}
}