当前位置:Java -> Rust 中的 Try 块

Rust 中的 Try 块

我之前写过一篇关于在Rust中错误管理的库。本周,我想写一下有关try块,这是一个实验性特性。

?运算符的限制

请查看上述文章,全面了解一般错误管理和?运算符。简而言之,?允许将其连接到返回Result的函数调用中:

  • 如果Result包含一个值,则继续正常执行
  • 如果它包含一个错误,则它会直接返回Result给调用函数。
fn add(str1: &str, str2: &str) -> Result<i8, ParseIntError> {
  Ok(str1.parse::<i8>()? + str2.parse::<i8>()?)
}

fn main() {
    print!("{:?}", add("1", "2"));
    print!("{:?}", add("1", "a"));
}


输出如下:

Ok(3)
Err(ParseIntError { kind: InvalidDigit })


请注意,定义函数的签名必须返回ResultOption。以下的代码块无法编译:

fn add(str1: &str, str2: &str) -> i8 {
  str1.parse::<i8>()? + str2.parse::<i8>()?
}


the `?` operator can only be used in a function that returns `Result` or `Option`


冗长的替代方案

我们必须手动解包以返回一个非包装类型,例如i8而不是Option

fn add(str1: &str, str2: &str) -> i8 {
  let int1 = str1.parse::<i8>();                  //1
  let int2 = str2.parse::<i8>();                  //1
  if int1.is_err() || int2.is_err() { -1 }        //2-3
  else { int1.unwrap() + int2.unwrap() }          //4
}


  1. 定义Result变量
  2. 手动检查变量中是否包含错误,,解析失败
  3. 返回默认值,因为我们无法获得Result。在这种情况下,这不是一个好主意,但这是为了解释
  4. 自信地解包

try块的拯救

上面的示例虽然可行,但相当冗长。try块是一种实验性方法,旨在使其更加优雅。它允许将所有的错误检查"压缩"到单个块中:

#![feature(try_blocks)]                           //1

fn add(str1: &str, str2: &str) -> i8 {
  let result = try {
    let int1 = str1.parse::<i8>();
    let int2 = str2.parse::<i8>();
    int1.unwrap()? + int2.unwrap()?               //2
  };
  if result.is_err() { -1 }                       //3
  else { result.unwrap() }                        //4
}


  1. 启用实验性特性
  2. 即使定义函数不返回Result,也使用?运算符
  3. 仅检查错误一次
  4. 自信地解包

然而,代码无法编译:

the `?` operator can only be applied to values that implement `Try`


i8不实现Try。既不是i8也不是Try属于我们的crate;自定义实现需要使用包装类型模式。幸运的是,已经有一些类型实现了TryResultOptionPollControlFlow

fn add(str1: &str, str2: &str) -> i8 {
  let result: Result<i8, ParseIntError> = try {   //1
    str1.parse::<i8>()? + str2.parse::<i8>()?     //2
  };
  if result.is_err() { -1 }
  else { result.unwrap() }
}


  1. 编译器无法推断类型
  2. try块中对Result使用?现在是允许的

try block

结论

我在20多年前的Java中了解了try块。Java需要它,因为异常是其错误处理系统的根本;而Rust不需要,因为它使用函数式编程来处理错误 —— 主要是Result

?运算符基于Result类型,允许在返回Result的函数中进行短路操作。如果函数不返回Result,则需要大量的样板代码。实验性的try块减少了其中的一些代码。

进一步了解

推荐阅读: ChatGPT是什么?

本文链接: Rust 中的 Try 块