我有这个代码。
if let Ok(file) = env::var("CONF") {
if let Ok(mut reader) = fs::File::open(&file) {
if let Ok(conf) = Json::from_reader(&mut reader) {
// do something with conf
}
}
}
我试图让它不像一个节日假日树,并考虑链接。请注意,此链中的每个步骤都会产生另一个Result
,所以显然这不起作用(我们在Result
中得到Result
)。
let conf = env::var("CONF")
.map(fs::File::open)
.map(Json::from_reader);
// do something with conf
此外,我的错误类型每个步骤都不同,这意味着我不能只用.map
替换.and_then
。
我想我正在寻找类似于JavaScript承诺的东西。也就是说,从承诺内部返回的承诺揭开了内在的承诺。签名可能应该是这样的:
impl<T, E> Result<T, E> {
fn map_unwrap<F, U, D>(&self, op: F) -> Result<U, D>
where F: FnOnce(T) -> Result<U, D>
}
Rust中有这样的机制吗?有没有另一种方法摆脱我的节日假日树?
Rust中有这样的机制吗?
是的 - 虽然不是像你提出的那样一次拍摄。让我们回顾一下你的理论签名:
impl<T, E> Result<T, E> {
fn map_unwrap<F, U, D>(&self, op: F) -> Result<U, D>
where
F: FnOnce(T) -> Result<U, D>,
{}
}
这不起作用 - 假设我们从Err
变体开始 - 这段代码如何知道如何从E
转换为D
?另外,&self
不适合想要转换类型的函数;那些通常采取self
。
您需要组合两个组件:
Result::and_then
impl<T, E> Result<T, E> {
fn and_then<U, F>(self, op: F) -> Result<U, E>
where
F: FnOnce(T) -> Result<U, E>,
{}
}
Result::map_err
impl<T, E> Result<T, E> {
fn map_err<F, O>(self, op: O) -> Result<T, F>
where
O: FnOnce(E) -> F,
{}
}
然后,您将需要一个可以表示两种错误类型的类型。我会懒惰并使用Box<Error>
结合在一起,您需要以下内容:
use std::env;
use std::fs::File;
use std::error::Error;
fn main() {
let conf = env::var("CONF")
.map_err(|e| Box::new(e) as Box<Error>)
.and_then(|f| File::open(f).map_err(|e| Box::new(e) as Box<Error>));
}
现在每个调用都将错误值转换为共享类型,结果可以使用and_then
进行链接。据推测,您的真实代码会创建一个适合您的问题的错误类型,然后您将在map_err
调用中使用它。我是implement From
,那么你可以拥有:
let conf: Result<_, Box<Error>> = env::var("CONF")
.map_err(Into::into)
.and_then(|f| File::open(f).map_err(Into::into));
如果您真的想忽略结果,就像使用if let
一样,您可以使用这样的宏:
macro_rules! iflet {
([$p:pat = $e:expr] $($rest:tt)*) => {
if let $p = $e {
iflet!($($rest)*);
}
};
($b:block) => {
$b
};
}
fn main() {
iflet!([Ok(file) = env::var("CONF")]
[Ok(mut reader) = File::open(&file)]
[Ok(conf) = Json::from_reader(&mut reader)] {
// do something with conf
});
}
Playground (without Json part)
该宏最初来自an answer I made to a similar question on Option
s,但它适用于任何if let
。虽然,与Result
你经常想以某种方式使用Err
,所以我通常倾向于the approach explained by Shepmaster或?
/ try!
。