Rust 依赖显式的错误管理:没有异常,而是使用返回值 Result<T, E>。这确保了代码的安全性和可预测性。
问题历史:
许多语言选择使用异常,这导致了意外情况和在运行时显式处理异常的需要。Rust 从一开始就致力于通过类型控制,所有错误都必须是函数签名的一部分。
问题:
主要任务是编写代码,使错误不被忽视或掩盖,没有 “崩溃” 的函数,但代码保持紧凑和可读。需要正确地将错误向上抛出,而不会丢失其类型信息,且不混淆逻辑。
解决方案:
关键工具是类型 Result<T, E> 和运算符 ?,它会自动 "展开" 结果:出现错误时,函数立即退出并返回错误,成功时返回值。
代码示例:
fn read_number(file: &str) -> Result<i32, std::io::Error> { let content = std::fs::read_to_string(file)?; let num: i32 = content.trim().parse()?; Ok(num) }
关键特性:
.map_err() 和其他方法)在返回 Unit (void) 的函数中可以使用 ? 吗?
不,可以使用的运算符 ? 仅允许在返回 Result 或 Option 的函数内部。如果函数返回 (),则不能使用 ?。
如果在多个 ? 调用中的错误类型不同,会发生什么?
会导致编译错误:错误类型必须明确确定。要么通过 .map_err() 将所有错误转换为统一类型,要么使用 thiserror,或者在 API 级别上描述 enum 包装。示例:
fn foo() -> Result<_, MyError> { let a = bar()?; let b = baz().map_err(MyError::Baz)?; Ok(…) }
在内部逻辑中使用 .unwrap() 有什么危险?
常见的误解是, "在主要" 代码中可以安全使用 unwrap(), "它不会崩溃"。事实上,即使是小的看不见的错误也会导致运行时恐慌——破坏程序的安全性。
unwrap/expect 捕获所有错误,尤其是在内部逻辑中在生产代码中留下了许多 unwrap() 进行快速调试。结果,在用户输入不正确时,任何解析故障都会导致应用程序崩溃。
优点:
缺点:
使用完整的 Result<T, E> 栈,明确描述错误,仅使用 ? 和 .map_err(),保留错误的所有信息。
优点:
缺点: