错误处理

Rust 中的两类错误处理:panic 和 Result.

普通错误使用 Result 类型来处理。Result 通常用以表示由程序外部的事物引发的错误,比如错误的输入、网络中断或权限问题。这些问题并不是意料之中的,在没有任何 bug 的程序中也可能会不时出现。

panic 针对的是另一种错误,即那种永远不应该发生的错误。

Panic

当程序遇到下列问题的时候,就可以断定程序自身存在 bug,故而会引发 panic

  • 数组越界访问
  • 整数除以 0
  • 在恰好为 Err 的 Result 上调用 .expect()
  • 断言失败。

panic!() 是一种宏,用于处理程序中出现错误的情况。panic 不是崩溃,也不是未定义行为。它更像是 Java 中的 RuntimeException

在 Panic 时也会出现异常,如果 Rust 在试图清理第一个 panic 时,.drop() 方法触发了第二个 panic,那么这个 panic 就是致命的。Rust 会停止展开调用栈并中止整个进程。

Result

例子 1 异常返回

fn get_user_name() -> Result<String, io::Error> {
    let mut user_name = String::new();
    println!("Enter your name: ");
    std::io::stdin().read_line(&mut user_name).unwrap();
    let trimmed_name = user_name.trim();
    println!("You entered: {}", user_name);
    if trimmed_name.eq("Fuck") {
        Err(io::Error::new(io::ErrorKind::InvalidInput, "Inappropriate language is not allowed."))
    } else {
        Ok(user_name.trim().to_string())
    }
}

例子 2 捕捉异常 match

fn main() {
    let user_name = get_user_name();
    match user_name {
        Ok(name) => println!("Hello, {}!", name),
        Err(e) => println!("Error: {}", e),
    }
}

例子 3 Result 别名

//默认情况下
fn get_user_name() -> Result<String,io::Error> {}
//别名情况下
fn get_user_name() -> io::Result<String> {}

例子 4 打印错误

Err(e) => println!("Error: {}", e),

例子 5 传播错误

大多数时候,当我们试图做某些可能失败的事情时,可能不想立即捕获并处理错误。如果在每个可能出错的地方都要使用十来行 match 语句,那代码就太多了。

Rust 的 ? 运算符可以执行此操作。这是一个写在方法中的语法糖.假如是 Main 方法会导致没有返回而异常,也可以直接使用 ? 进行解包.

let weather = get_weather(hometown)?;

let weather = get_weather(hometown).ok()?;

Cannot use the `?` operator in a function that returns `()` [E0277]

例子 6 不可能出现的错误

如果我们正在编写的代码已经返回了 GenericResult,那么就可以添加一个 ?,并且忽略这个错误。否则,我们将不得不为处理不可能发生的错误而烦恼。最好的选择是使用 Result 的 .unwrap() 方法。如果结果是 Err,就会 panic;但如果成功了,则会直接返回 Ok 中的成功值:

let num = digits.parse::().unwrap();

例子 7 忽略错误

有时我们只想完全忽略一个错误。惯用法 let _ = ... 可用来消除这种警告。

let _ = writeln!(stderr(), "error: {}", err);  // 正确,忽略结果

例子 8 处理 main() 中的错误

如果你传播错误的距离足够远,那么最终它就会抵达 main(),后者必须对其进行处理。通常来说,main() 不能使用 ?,因为它的返回类型不是 Result:

处理 main() 中错误的最简单方式是使用 .expect():

fn main() {
    calculate_tides().expect("error");  // 责任止于此
}

fn main() -> Result<(), TideCalcError> {
    let tides = calculate_tides()?;
    print_tides(tides);
    Ok(())
}

例子 9 声明自定义错误类型

假设你正在编写一个新的 JSON 解析器,并且希望它有自己的错误类型。

return Err(JsonError {
    message: "expected ']' at end of array".to_string(),
    line: current_line,
    column: current_column
});

use std::fmt;

// 错误应该是可打印的
impl fmt::Display for JsonError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{} ({}:{})", self.message, self.line, self.column)
    }
}

// 错误应该实现std::error::Error特型,但使用Error各个方法的默认定义就够了
impl std::error::Error for JsonError { }