我应该放弃使用 `and_then` 并始终使用 `?` 运算符吗?
Should I ditch using `and_then` and always use the `?` operator?
我想写这样的东西,但由于类型不匹配,它无法编译:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let val = std::env::args()
.nth(1)
.ok_or("1 arg is expected")
.and_then(std::fs::File::open)
.and_then(serde_yaml::from_reader)?;
}
因为在每个闭包中添加一个 map_err
看起来很慢而且 'boiler platy',我用类似的东西替换它:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let val = serde_yaml::from_reader(std::fs::File::open(
std::env::args().nth(1).ok_or("1 arg is expected")?,
)?)?;
}
第一个感觉更自然,读起来像英文,而第二个感觉有点倒退。
我是否应该放弃 and_then
并始终使用 ?
运算符?
如果不是,有没有办法使结果组合器像 ?
运算符一样平滑?
Should I ditch the and_then and always use the ? operator ?
这是个人判断,只有你能回答。
Is there a way to make result combinator as smooth as the ? operator ?
坦白说没有。 ?
« 隐式 » 执行转换(不是 真正地 隐式,因为它是其工作的重要组成部分,但不必单独调用转换,也许是 « 简洁地 »? ), and_then
没有。这意味着在使用 and_then
时,您必须自己执行这些转换。这似乎合乎逻辑。
您也许可以为此创建一个方便的宏。或者添加一个扩展方法或包装器类型,可以在幕后执行这些转换。
您的第一个表达式不起作用的原因是错误类型不匹配。更具体地说,
let val = std::env::args()
.nth(1)
.ok_or("1 arg is expected")
.and_then(std::fs::File::open)
.and_then(serde_yaml::from_reader)?;
std::env::args().nth(1)
return一个Option<T>
。如果您查看 ok_or
签名,即 pub fn ok_or<E>(self, err: E) -> Result<T, E>
。这意味着对于您的情况,ok_or("1 arg is expected")
您的 return 类型是 Result<T, &str>
。所以你这里的错误类型是 &str
因为在 ok_or 中你传递了一个字符串切片作为错误类型。
如果你看一下 and_then
方法,签名是 pub fn and_then<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> Result<U, E>
,基本上这意味着你传入的 Function and_then 应该与原来的错误类型相同结果,是 &str
。这与行 .and_then(serde_yaml::from_reader)
相同,所有“链接”and_then 函数的错误类型需要一致。
如果你真的想要,你可以通过以下方式让你的代码通过编译。或者你可以创建一个统一的错误,这样就没有不匹配的错误类型。然后就可以用ok_or(...).and_then(...).and_then(...)
那种写法了。您只需要匹配错误类型即可。
工作示例
fn custom_fs_open(path: String) -> Result<String, &'static str>{
// actual logics to open fs
Ok(String::from("abc"))
}
fn custom_serde_yaml(buf: String) -> Result<String, &'static str>{
// actual logics to do serde convertion
Ok(String::from("cde"))
}
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let val = std::env::args()
.nth(1)
.ok_or("1 arg is expected")
.and_then(custom_fs_open)
.and_then(custom_serde_yaml)?;
Ok(())
}
我想写这样的东西,但由于类型不匹配,它无法编译:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let val = std::env::args()
.nth(1)
.ok_or("1 arg is expected")
.and_then(std::fs::File::open)
.and_then(serde_yaml::from_reader)?;
}
因为在每个闭包中添加一个 map_err
看起来很慢而且 'boiler platy',我用类似的东西替换它:
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let val = serde_yaml::from_reader(std::fs::File::open(
std::env::args().nth(1).ok_or("1 arg is expected")?,
)?)?;
}
第一个感觉更自然,读起来像英文,而第二个感觉有点倒退。
我是否应该放弃 and_then
并始终使用 ?
运算符?
如果不是,有没有办法使结果组合器像 ?
运算符一样平滑?
Should I ditch the and_then and always use the ? operator ?
这是个人判断,只有你能回答。
Is there a way to make result combinator as smooth as the ? operator ?
坦白说没有。 ?
« 隐式 » 执行转换(不是 真正地 隐式,因为它是其工作的重要组成部分,但不必单独调用转换,也许是 « 简洁地 »? ), and_then
没有。这意味着在使用 and_then
时,您必须自己执行这些转换。这似乎合乎逻辑。
您也许可以为此创建一个方便的宏。或者添加一个扩展方法或包装器类型,可以在幕后执行这些转换。
您的第一个表达式不起作用的原因是错误类型不匹配。更具体地说,
let val = std::env::args()
.nth(1)
.ok_or("1 arg is expected")
.and_then(std::fs::File::open)
.and_then(serde_yaml::from_reader)?;
std::env::args().nth(1)
return一个Option<T>
。如果您查看 ok_or
签名,即 pub fn ok_or<E>(self, err: E) -> Result<T, E>
。这意味着对于您的情况,ok_or("1 arg is expected")
您的 return 类型是 Result<T, &str>
。所以你这里的错误类型是 &str
因为在 ok_or 中你传递了一个字符串切片作为错误类型。
如果你看一下 and_then
方法,签名是 pub fn and_then<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> Result<U, E>
,基本上这意味着你传入的 Function and_then 应该与原来的错误类型相同结果,是 &str
。这与行 .and_then(serde_yaml::from_reader)
相同,所有“链接”and_then 函数的错误类型需要一致。
如果你真的想要,你可以通过以下方式让你的代码通过编译。或者你可以创建一个统一的错误,这样就没有不匹配的错误类型。然后就可以用ok_or(...).and_then(...).and_then(...)
那种写法了。您只需要匹配错误类型即可。
工作示例
fn custom_fs_open(path: String) -> Result<String, &'static str>{
// actual logics to open fs
Ok(String::from("abc"))
}
fn custom_serde_yaml(buf: String) -> Result<String, &'static str>{
// actual logics to do serde convertion
Ok(String::from("cde"))
}
fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let val = std::env::args()
.nth(1)
.ok_or("1 arg is expected")
.and_then(custom_fs_open)
.and_then(custom_serde_yaml)?;
Ok(())
}