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


📝 本文首发于 栏轩·阁

欢迎访问阅读原文,获取更好的阅读体验。


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


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

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

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 就是干这个的:

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

2.1 match 处理

rust 复制代码
let f = File::open("hello.txt");

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

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

2.2 快捷方法

rust 复制代码
// 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,就把里面的值取出来:

rust 复制代码
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)
}

没有 ? 的话要写成:

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

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

rust 复制代码
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 的函数中:

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

OptionResult------.ok_or()

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

ResultOption------.ok()

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

5. 链式组合

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

rust 复制代码
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 代码。

相关推荐
Amos_Web4 小时前
Rspack 源码解析(十):Hash 与 Asset 生成
前端·rust·源码阅读
柯南46684 小时前
【AI开发之Rust】第 12 课:并发模型与同步原语
rust·编程语言
音符犹如代码1 天前
RustFS 1.0.0 深度解析:GA 之后,它值得替代 MinIO 吗?
rust
qq_452396231 天前
第十三篇:《unsafe Rust 与 FFI:与 C 和系统调用交互》
rust
传奇开心果编程1 天前
【Rust入门练中学】 第1课:从零开始
开发语言·学习·rust
对象存储与RustFS1 天前
跨实现迁移 MinIO→RustFS:mc diff 静默返回才是最容易翻车的一步
后端·rust·开源
doiito(Do It Together)1 天前
【Agent Harness】Gliding Horse 最新进化:从“能学习”到“可验证的自主进化”
人工智能·rust·系统架构·开源