考虑一下:
loop {
let data = match something() {
Err(err) => {
warn!("An error: {}; skipped.", err);
continue;
},
Ok(x) => x
};
let data2 = match somethingElse() {
Err(err) => {
warn!("An error: {}; skipped.", err);
continue;
},
Ok(x) => x
};
// and so on
}
如果我不需要将ok值分配给data
,我就会使用if let Err(err) = something()
,但是上面的代码是否有快捷方式?&d; d我认为,避免复制粘贴Err / Ok分支,这是典型的情况吗?类似if let
的东西也会返回ok值。
答案 0 :(得分:7)
如果您要经常“解包或继续”结果,请考虑将该逻辑封装在单独的函数中。有了它,您可以利用?
语法来引发函数之外的错误。然后可以将循环的流逻辑写在一个地方(尽管此时,您可能不再需要continue
)。
loop {
if let Err(err) = do_event() {
warn!("An error: {}; skipped.", err);
// continue; // you also don't need this
}
}
fn do_event() -> Result<(), YourErrorType> {
let data = do_something()?; //
let x = something_more()?; // error propagation!
Ok(())
}
答案 1 :(得分:5)
虽然我认为E_net4的答案可能是最好的,但我为后代添加了一个宏,以防创建一个单独的函数并且使用?
运算符提前返回理由不受欢迎。
这是一个简单的skip_fail!
宏,continue
在传递错误时包含循环:
macro_rules! skip_fail {
($res:expr) => {
match $res {
Ok(val) => val,
Err(e) => {
warn!("An error: {}; skipped.", e);
continue;
}
}
};
}
此宏可用作let ok_value = skip_fail!(do_something());
同样,我相信在一个单独的函数中使用?
,并且如果没有任何失败则返回Ok(end_result)
可能是最惯用的解决方案,所以如果你可以使用那个答案,你可能应该这样做。 / p>
答案 2 :(得分:4)
如果您必须将多个Ok
链接在一起,则需要在下一个操作中使用一个Ok
的值,而不必关心 where < / em>在链中出现错误,请考虑and_then
:
loop {
let outcome = something()
.and_then(|a| something_else(a))
.and_then(|a| another_thing(a))
.and_then(|a| {
let b = a + salt;
one_more(b)
});
if let Err(e) = outcome {
warn!("An error: {}; skipped.", e);
}
}
something
,something_else
,another_thing
和one_more
都返回某种形式的Result
。即使此示例删除continue
语句,and_then
在Result
类型为Err
时通过短路有效地模拟它。所有进一步的调用都将被跳过。
通过对只需要一个函数调用的语句使用非闭包,可以使这更简洁:
loop {
let outcome = something()
.and_then(something_else)
.and_then(another_thing)
.and_then(|a| one_more(a + salt));
if let Err(e) = outcome {
warn!("An error: {}; skipped.", e);
}
}
(注意函数上没有括号,表示它们被用作可调用对象而不是它们的返回值)
答案 3 :(得分:2)
如果您愿意使用不稳定的功能,可以为此使用try块:
#![feature(try_blocks)]
pub fn something() -> Result<String, String> {
Err(String::from("Badness"))
}
pub fn something_else() -> Result<String, String> {
Ok(String::from("Ok"))
}
pub fn main() {
loop {
let result: Result<(), String> = try {
let data = something()?;
let data2 = something_else()?;
};
if let Err(e) = result {
println!("An error: {}; skipped.", e)
}
}
}
正如shepmaster在评论中提到的那样,可以使用立即评估的闭包(即立即调用的函数表达式,简称IIFE)来完成此操作而不会产生任何不稳定的特征。这是MutantOctopus在解决方案注释中提出的E_net4解决方案的修改。
pub fn something() -> Result<String, String> {
Err(String::from("Badness"))
}
pub fn something_else() -> Result<String, String> {
Ok(String::from("Ok"))
}
pub fn main() {
loop {
let result: Result<(), String> = (|| {
let data = something()?;
let data2 = something_else()?;
Ok(())
})();
if let Err(e) = result {
println!("An error: {}; skipped.", e)
}
}
}