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() // 创建替代文件
});

unwrap 和 expect 适合原型阶段快速验证,生产代码建议用 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)
}

关键:? 只能用在返回 Result 或 Option 的函数里。 在 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
}

Option 转 Result------.ok_or():

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

Result 转 Option------.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 代码。

相关推荐
轻舟技术A9 小时前
Pi 的 Rust 实现来了:rpi,不止一个终端 Agent
开发语言·后端·rust
wflynn12 小时前
GitHub 今日推荐|wordcraft:用 Rust 重写 Word 内核并开放给 AI 调用
rust·开源·github
I'm Jie13 小时前
【开源】7 天,我用 Rust 复刻了 FinalShell!Rhost v1.0.0 发布
开发语言·rust·开源
小灰灰搞电子14 小时前
Rust+Slint 实现一个终端软件源码分享(半成品)
开发语言·rust
恋喵大鲤鱼15 小时前
Rust str 与 String 类型的区别
rust
miofly15 小时前
GitHub 今日推荐|fsearch:Rust 打造 macOS 毫秒级全盘文件名与内容搜索工具
rust·开源·github
大鹏的NLP博客16 小时前
Rust 的本质:用数学约束实现编译期内存安全
安全·rust
Kapaseker17 小时前
Rust 读多写少还在用 Mutex?别让线程白白排队
rust
EatFan17 小时前
2026 Rust 后端技术栈选型:Axum 0.8 + Tokio + SQLx 全链路怎么搭
开发语言·后端·rust·tokio·serde·axum·sqlx
Zoom1 天前
【开源】7 天,我用 Rust 复刻了 FinalShell!Rhost v1.0.0 发布
rust·electron·shell