返回介绍

6.2 可恢复

发布于 2024-10-13 11:25:29 字数 3313 浏览 0 评论 0 收藏 0

可恢复性(recoverable)错误通常指一种状态。比如打开文件时,没找到,应该新建。

显然,最终如何处理,由调用方负责。

没有异常(exception),对于可恢复性错误,返回 Result<T, E> 对象。

  • 通过函数签名,很容易知道是否会发生错误。
  • 编译器对调用返回值进行检查,警告未处理行为。
  • 相比 C 和 Go,Rust 有更优雅的错误传播方式。

和元组一次返回多个值不同, Result 要么返回 Ok ,要么返回 Err

它们都携带有载荷,代表返回值或具体错误。

enum Result<T, E> {
  Ok(T),
  Err(E),
}

match 匹配和解构较为繁琐,可直接调用 Result 相关方法。

  • is_ok , is_err
  • ok , err
  • unwrap , except
  • unwrap_or , unwrap_or_else
  • as_ref , as_mut
fn test(x: i32) -> Result<i32, &'static str> {
  if x > 0 { Ok(x) } else { Err("error") }
}

fn main() {
  match test(1) {
    Ok(x)  => println!("{:?}", x),
    Err(e) => println!("{:?}", e),
  }

  let _x = test(0).unwrap_or_else(|err| {  // unwrap, expect
    panic!("{:?}", err);
  });
}                    

提示:

  • 创建有意义的别名。 type io::Result<T> = Result<T, io::Error>
  • 定义具体错误类型。 Result<T, io::Error>
  • 只是判断是否出错, Result<(), error>

传播

通过 return Err 将错误按调用堆栈传递出去。

fn div(x: i32, y: i32) -> Result<i32, &'static str> {
  if y != 0 { 
    Ok(x / y) 
  } else { 
    Err("divide by zero") 
  }
}

fn test(x: i32, y: i32) -> Result<i32, &'static str> {
  let z = div(x, y);
  if z.is_err() { return z; }  // 传播!

  z
}


fn main() {
  test(3, 0).unwrap();    // 处理错误!
}                    

? 操作符简化传播代码。

  • 用于 Result<T, E> 返回类型的函数调用。
  • 解构 Ok 值,或立即 return Err 继续传播。
  • 传播 Err 关联类型必须相同(或自动转换)。

使用 ? 操作符,意味着当前函数(caller)也必须返回 Result<T, E> 类型。

其中 T 类型可不同,但 E 必须相同。如不想继续传播,可调用 unwrap 等方法。

也可用于返回 Option<T> 的函数调用,区别在于向外 return None

fn test(x: i32, y: i32) -> Result<i32, &'static str> {
  let z: i32 = div(x, y)?;
  Ok(z)
}

//  let z: i32 = match div(x, y) {
//    Ok(v)  => v,
//    Err(e) => return Err(e),
//  }

链式调用。

use std::fs::File;
use std::io::prelude::*;

fn test() -> std::io::Result<()> {
  let mut contents = String::new();
  File::open("foo.txt")?.read_to_string(&mut contents)?;
  Ok(())
}

fn main() {
  
  // 无法使用 ? 操作符,除非修改 main 签名,
  // 改为 fn main() -> Result<(), E>。
  
  test().unwrap();
}                    

集中处理。

use std::io;
use std::io::prelude::*;
use std::fs::File;

fn main() {

  // 匿名函数调用。
  (|| -> io::Result<()> {
    let mut f = File::open("foo.txt")?;  // Result<File>
    let mut buf = [0; 10];

    let n = f.read(&mut buf)?;       // Result<usize>
    println!("{:?}", &buf[..n]);
    
    Ok(())
  })().unwrap_or_else(|e| {
    // 集中错误处理。
    println!("{:?}", e);
  });

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文