本页目录

Result 与 Option

Option 表示"值可能不存在",Result 表示"操作可能失败"——两者都是枚举,不是 exception。? 运算符在遇到 None/Err 时提前返回,combinator 链(map/and_then/or_else)让错误处理变成类型安全的管道而非 try-catch 的控制流跳跃。

Option: 值可能不存在

fn find_user(id: u64) -> Option<User> { ... }

find_user(42)
    .map(|u| u.name)                 // Option<User> → Option<String>, transform
    .and_then(|name| db_lookup(&name)) // Option<String> → Option<Record>, chain
    .filter(|r| r.active)            // keep only if active
    .unwrap_or(default_record);      // extract or use default

每个 combinator 只处理 Some 情况——None 自动短路。这个模式在函数式编程中称为"railway oriented programming":要么沿着成功轨道传递,要么一瞬间跳到失败轨道并保持在那里,全程不检查 null。编译器在 unwrap_or 处强制你提供 default——不能忘记处理缺失值。

Result: 错误是值,不是异常

fn parse(s: &str) -> Result<i32, ParseIntError> { s.parse() }

let n = match parse("42") {
    Ok(n) => n * 2,
    Err(e) => { eprintln!("parse failed: {}", e); return; }
};

与异常的核心区别:⁠错误是返回值的一部分⁠,调用者从函数签名就知道它可能失败。没有隐藏的控制流,没有需要记忆的"这个函数抛出什么异常"。编译器强制你处理 Result——#[must_use] 属性让未使用的 Result 产生编译警告。

? 运算符: 错误的早返回

fn read_config(path: &str) -> Result<Config, Error> {
    let file = std::fs::read_to_string(path)?;    // Err → early return
    let config: Config = toml::from_str(&file)?;
    Ok(config)
}

展开等价于:

let file = match std::fs::read_to_string(path) {
    Ok(v) => v,
    Err(e) => return Err(e.into()),  // .into() 通过 From trait 转换错误类型
};

? 调用 From::from 做自动错误类型转换——只要实现了 From<io::Error> for MyErrorstd::fs::read_to_string()?io::Error 会自动转为 MyError。这就是 Rust 错误传播的关键——不需要在每一层手动 wrap 错误。

main 返回 Result

fn main() -> Result<(), Box<dyn Error>> {
    let config = read_config()?;     // ? in main!
    Ok(())
}

main 返回 Result 时,如果 main 返回 Err,运行时打印错误并退出(exit code 1)。适合简单的 CLI 工具。复杂应用通常有自己的错误处理和日志。

参考

  • Rust Book: Chapter 9
  • Rust By Example: Option/Result, ?

Keywords: Option, Result, combinator, and_then, ?, error propagation, From trait, #[must_use]