Rust学习笔记:错误处理——panic!、Result、Option与_运算符

Rust 没有 try-catch,没有异常。它的错误处理分两类:不可恢复的直接崩(panic!),可恢复的用 Result<T, E> 返回值,编译器强制你处理。这一篇把两种方式讲透。


1. panic!:不可恢复的错误

panic! 是 Rust 的「崩了」——遇到无法继续的情况,程序直接终止:

let v = vec![1, 2, 3];
// let x = v[99]; // panic: index out of bounds

和 C 的段错误不同,panic! 会打印调用栈,告诉你崩在第几行。只在真正的 bug(数组越界、除零)时才 panic,业务错误别用。


2. Result<T, E>:可恢复的错误

文件不存在、网络超时、输入格式不对——这些不该崩,应该返回错误让调用方决定怎么办。Result 就是干这个的:

enum Result<T, E> {
    Ok(T),   // 成功,带返回值
    Err(E),  // 失败,带错误信息
}

2.1 match 处理

let f = File::open("hello.txt");

match f {
    Ok(file) => println!("文件打开成功"),
    Err(error) => println!("文件打开失败: {error}"),
}

编译器知道你收到了 Result,不处理两种情况之一就编译不过——和 Option 一样,编译期安全网。

2.2 快捷方法

// unwrap:成功返回值,失败直接 panic
// let f = File::open("not_exist.txt").unwrap(); // panic!

// expect:和 unwrap 一样,但可以自定义 panic 信息
// let f = File::open("not_exist.txt").expect("必须存在这个文件");

// unwrap_or_else:失败时执行一个闭包
let f = File::open("not_exist.txt").unwrap_or_else(|err| {
    println!("无法打开: {err}");
    File::create("fallback.txt").unwrap() // 创建替代文件
});

unwrapexpect 适合原型阶段快速验证,生产代码建议用 match?


3. ? 运算符:错误传播

多层函数调用时,每层都写 match 来处理错误再 return Err(...) 会很冗长。? 做了这件事——如果值是 Err,直接 return Err;如果是 Ok,就把里面的值取出来:

fn read_file_content() -> Result<String, io::Error> {
    let mut f = File::open("hello.txt")?; // 失败就直接 return Err
    let mut content = String::new();
    f.read_to_string(&mut content)?;
    Ok(content)
}

没有 ? 的话要写成:

let mut f = match File::open("hello.txt") {
    Ok(file) => file,
    Err(e) => return Err(e),
};

? 把它压缩成了 File::open("hello.txt")? 一行。链式调用更简洁:

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

关键:? 只能用在返回 ResultOption 的函数里。main 函数或其他返回 () 的地方不能直接用 ?


4. Option 与 Result 互转

Option 也可以配合 ?,但只能在返回 Option 的函数中:

fn find_first_even(nums: &[i32]) -> Option<i32> {
    for &n in nums {
        if n % 2 == 0 {
            return Some(n);
        }
    }
    None
}

OptionResult——.ok_or()

let result = find_first_even(&nums).ok_or("没找到偶数");
// Ok(8) 或 Err("没找到偶数")

ResultOption——.ok()

let opt = File::open("hello.txt").ok(); // 成功变 Some(file),失败变 None

5. 链式组合

多个可能出错的操作用 ? 串起来,逻辑清晰:

fn process_data() -> Result<i32, String> {
    let nums = vec![10, 20, 30];
    let first = nums.get(0).ok_or("列表为空")?;
    let second = nums.get(1).ok_or("只有1个元素")?;
    Ok(first + second)
}

每一步都可能失败,但代码读起来像成功路径一样流畅。这比嵌套 if-else 或 try-catch 块要舒服得多。


总结

机制 用途 编译期检查?
panic! 不可恢复的 bug 否(运行时)
Result 可恢复的错误,调用方处理
Option 值可能不存在
? 简化 Result/Option 传播

核心思想:能用 Result/Option 就别用 panic!,能用 ? 就别堆 match

至此,Rust 基础语法的「必修课」已经全部覆盖。下一篇进入泛型与 Trait,开始写真正可复用的 Rust 代码。

posted @ 2026-09-19 09:47  PC2005-cloud  阅读(6)  评论(0)    收藏  举报