blog.dopana

Back

这是Rust基础系列的第10篇文章(终结篇)。Rust将错误分为两大类:使用 panic! 处理的不可恢复错误,以及使用 Result<T, E> 处理的可恢复错误。

使用 panic! 处理不可恢复错误#

当程序遇到无法继续运行的致命情况(如数组越界访问)时,Rust会执行 panic! 宏。这会打印失败信息、展开栈(unwind stack)并退出进程:

fn main() {
    // panic!("崩溃并终止");
    let v = vec![1, 2, 3];
    // v[99]; // 自动触发 panic
}
rust

仅在程序遇到无法自行修复的非法状态时使用 panic!

使用 Result 处理可恢复错误#

对于可能在预料之中失败的操作(如打开文件或解析整数),函数会返回 Result<T, E>

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

你可以使用 match 来检查 Result

use std::fs::File;

fn main() {
    let greeting_file_result = File::open("hello.txt");

    let _greeting_file = match greeting_file_result {
        Ok(file) => file,
        Err(error) => panic!("打开文件时遇到问题:{error:?}"),
    };
}
rust

快捷方式:unwrapexpect#

Result 提供了提取 T 或在遇到 Err 时触发 panic 的便捷方法:

let file = File::open("hello.txt").unwrap();
let file = File::open("hello.txt").expect("hello.txt 应该存在于当前目录中");
rust

在生产环境中推荐优先使用 expect 而不是 unwrap,因为它能在崩溃日志中提供有价值的上下文。

使用 ? 运算符传递错误#

编写函数时,你常常希望将错误向上传递给调用者,而不是在当前函数中立即处理。? 运算符提供了优雅且简洁的错误传递语法:

use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    let mut username = String::new();
    File::open("hello.txt")?.read_to_string(&mut username)?;
    Ok(username)
}

fn main() -> Result<(), io::Error> {
    let username = read_username_from_file()?;
    println!("用户名:{username}");
    Ok(())
}
rust

如果使用 ? 评估的操作返回了 Err,该 Err 会立刻作为当前函数的返回值被返回。

总结#

Rust的错误处理非常明确:panic! 应对不可恢复的致命错误,Result<T, E> 管理可恢复错误,而 ? 运算符实现了人体工程学般的优雅传递。至此,全套10篇Rust基础系列教程全部圆满完成!

参考资料#