try!
的其他用法
这是一个很简单且常见的操作,要是它能够省略的话将会相当方便。可惜的是,因为 and_then
不够灵活,所以它不能。但是,我们可改用 try!
。
try!
在前面已经解释过,它可以充当 unwrap
或 return Err(err)
,这说法只是很大程度上是对的。实际上它意味着 unwrap
或者 return Err(From::from(err))
。由于 From::from
是一个不同类型间相互转换的工具,所以如果你使用 try!
,当中的错误若能够转换成返回类型,这将会自动转换。
use std::num::ParseIntError;
use std::fmt;
type Result<T> = std::result::Result<T, DoubleError>;
#[derive(Debug)]
enum DoubleError {
EmptyVec,
Parse(ParseIntError),
}
// 实现从 `ParseIntError` 到 `DoubleError` 的转换。如果一个 `ParseIntError`
impl From<ParseIntError> for DoubleError {
fn from(err: ParseIntError) -> DoubleError {
DoubleError::Parse(err)
}
}
impl fmt::Display for DoubleError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
DoubleError::EmptyVec =>
write!(f, "please use a vector with at least one element"),
DoubleError::Parse(ref e) => e.fmt(f),
}
}
}
// 和前面的结构一样,但没有将全部的 `Results` 和 `Options` 链接在一起,
// 我们使用 `try!` 立即得到内部的值。
// (原文:// The same structure as before but rather than chain all `Results`
// and `Options` along, we `try!` to get the inner value out immediately.)
fn double_first(vec: Vec<&str>) -> Result<i32> {
// 仍然转为 `Result`,通过规定怎样转为 `None`。
// (原上:// Still convert to `Result` by stating how to convert `None`.)
let first = try!(vec.first().ok_or(DoubleError::EmptyVec));
let parsed = try!(first.parse::<i32>());
Ok(2 * parsed)
}
fn print(result: Result<i32>) {
match result {
Ok(n) => println!("The first doubled is {}", n),
Err(e) => println!("Error: {}", e),
}
}
fn main() {
let numbers = vec!["93", "18"];
let empty = vec![];
let strings = vec!["tofu", "93", "18"];
print(double_first(numbers));
print(double_first(empty));
print(double_first(strings));
}
现在变得整洁多了。如果和原始的 panic
进行比较,可以看到它好像就是使用 try!
替换 unwrap
,除了返回类型是 Result
类型这点不同,所以它们必须在顶层被解构出来。
然而,不要指望这种错误处理经常取代 unwrap
的用法。这种错误处理会使代码行数扩大三倍,即便是很小的一段代码也不能称之为简单。
很多库可能只是抛弃了实现 Display
,然后在所需的基础上增加 From
(原文:Many libraries might get away with only implementing Display
and then adding From
on an as needed basis.)。然而,越是正式的库最后越是需要面临实现更高级的错误处理的需求。