? 的其他用法

注意在上一个例子中,我们调用 parse 后总是立即把错误从标准库错误 map 到装箱的错误。

  1. .and_then(|s| s.parse::<i32>()
  2. .map_err(|e| e.into())

因为这个操作很简单常见,如果有省略写法就好了。and_then 不够灵活,所以不能实现 这样的写法。不过,我们可以使用 ? 来代替它。

之前我们说 ? 就是 “要么 unwrap 要么 return Err(error)”,这大部分是对的,但 事实上 ? 是 “要么 unwrap 要么 return Err(From::from(err))”。From::from 是 不同类型间的转换工具,也就是说,如果在错误能够转换成返回类型的地方使用 ?,它就 会自动转换成返回类型。

这里,我们使用 ? 重写之前的例子。这样,只要为我们的错误类型实现 From::from,就 可以不再使用 map_err

  1. use std::error;
  2. use std::fmt;
  3. // 为 `Box<error::Error>` 取别名。
  4. type Result<T> = std::result::Result<T, Box<error::Error>>;
  5. #[derive(Debug)]
  6. struct EmptyVec;
  7. impl fmt::Display for EmptyVec {
  8. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
  9. write!(f, "invalid first item to double")
  10. }
  11. }
  12. impl error::Error for EmptyVec {
  13. fn description(&self) -> &str {
  14. "invalid first item to double"
  15. }
  16. fn cause(&self) -> Option<&error::Error> {
  17. // 泛型错误,没有记录内部原因。
  18. None
  19. }
  20. }
  21. // 这里的结构和之前一样,但是这次没有把所有的 `Results` 和 `Options` 串起来,
  22. // 而是使用 `?` 立即得到内部值。
  23. fn double_first(vec: Vec<&str>) -> Result<i32> {
  24. let first = vec.first().ok_or(EmptyVec)?;
  25. let parsed = first.parse::<i32>()?;
  26. Ok(2 * parsed)
  27. }
  28. fn print(result: Result<i32>) {
  29. match result {
  30. Ok(n) => println!("The first doubled is {}", n),
  31. Err(e) => println!("Error: {}", e),
  32. }
  33. }
  34. fn main() {
  35. let numbers = vec!["42", "93", "18"];
  36. let empty = vec![];
  37. let strings = vec!["tofu", "93", "18"];
  38. print(double_first(numbers));
  39. print(double_first(empty));
  40. print(double_first(strings));
  41. }

这段代码现在已经很清晰了。相比原始的 panic,它就像是把所有的 unwrap 调用都换 成 ? 一样。与 panic 相比,这样做的区别在于返回类型是 Result,因而必须在顶层 解构它们。

参见:

From::from and ?