如何在 Rust 中进行错误处理以及常见的陷阱是什么?

How to do error handling in Rust and what are the common pitfalls?

我注意到 Rust 没有例外。如何在 Rust 中进行错误处理以及常见的陷阱是什么?有没有办法通过加注、接球、再加注和其他东西来控制流量?我发现这方面的信息不一致。

Rust 通常通过两种方式解决错误:

  • 不可恢复的错误。一旦你 panic!,就是这样。你的程序或线程中止是因为它遇到了它无法解决的问题并且它的不变量被违反了。例如。如果您发现应该是 UTF-8 字符串的无效序列。

  • 可恢复的错误。在某些文档中也称为失败。你没有恐慌,而是发出 Option<T> or Result<T, E>。在这些情况下,您可以分别选择有效值 Some(T)/Ok(T) 或无效值 None/Error(E)。一般用None代替null,表示缺​​少值


困难的部分来了。申请。

展开

有时处理 Option 是一件令人头疼的事,您几乎可以保证得到一个值而不是一个错误。

在这些情况下,使用 unwrap 完全没问题。 unwrapSome(e)Ok(e)变成e,否则就panic了。 Unwrap 是一种将可恢复错误变为不可恢复错误的工具。

if x.is_some() {
    y = x.unwrap(); // perfectly safe, you just checked x is Some
}

if-block 内,它完全可以展开,因为它永远不会恐慌,因为我们已经用 x.is_some().

检查过它是 Some

如果您正在编写一个库,不鼓励使用 unwrap,因为当它出现恐慌时用户无法处理错误。此外,未来的更新可能会更改不变量。想象一下,如果上面的例子有 if x.is_some() || always_return_true()。不变量会改变,unwrap 可能会出现恐慌。

?运算符/try!

什么是 ? 运算符或 try! 宏?一个简短的解释是,它要么 returns 是 Ok() 中的值,要么过早地 returns 错误。

这里是运算符或宏展开的简化定义:

macro_rules! try {
    ($e:expr) => (match $e {
        Ok(val) => val,
        Err(err) => return Err(err),
    });
}

如果你这样使用它:

let x = File::create("my_file.txt")?;
let x = try!(File::create("my_file.txt"));

它将把它转换成这样:

let x = match File::create("my_file.txt") {
    Ok(val)  => val,
    Err(err) => return Err(err),
};

缺点是你的函数现在 return Result.

组合器

Option and Result 有一些方便的方法,允许以可理解的方式链接和处理错误。 andand_thenoror_elseok_ormap_err 等方法

例如,您可以有一个默认值,以防您的值被篡改。

let x: Option<i32> = None;
let guaranteed_value = x.or(Some(3)); //it's Some(3)

或者,如果您想将 Option 变成 Result

let x = Some("foo");
assert_eq!(x.ok_or("No value found"), Ok("foo"));

let x: Option<&str> = None;
assert_eq!(x.ok_or("No value found"), Err("No value found"));

这只是对您可以执行的操作的简要介绍。如需更多解释,请查看:

如果你需要终止一些独立的执行单元(一个网络请求,一个视频帧处理,一个GUI事件,一个要编译的源文件)而不是完整的你的应用程序,有一个函数std::panic::catch_unwind 调用闭包,如果发生恐慌,则捕获解除恐慌的原因。

let result = panic::catch_unwind(|| {
    panic!("oh no!");
});
assert!(result.is_err());

我不会授予此闭包对任何可能比它更长寿的变量或任何其他全局状态的写访问权。

文档还说该函数也可能无法捕获某些类型的恐慌。