langShift

错误处理与类型安全

学习 Rust 的错误处理机制,包括 Result、Option 类型和错误传播,对比 JavaScript 的异常处理

错误处理与类型安全

📖 学习目标

理解 Rust 的错误处理哲学,学会使用 ResultOption 类型,掌握错误传播模式,对比 JavaScript 的异常处理机制。


🎯 错误处理哲学对比

JavaScript 的异常处理

JavaScript 使用 try-catch 机制处理异常:

正在加载...

Rust 的错误处理

Rust 使用类型系统处理错误,没有异常机制:

正在加载...

错误处理差异

  1. 异常 vs 类型: JavaScript 使用异常,Rust 使用类型系统
  2. 运行时 vs 编译时: JavaScript 运行时检查,Rust 编译时检查
  3. 可恢复 vs 不可恢复: Rust 区分可恢复错误(Result)和不可恢复错误(panic)
  4. 显式 vs 隐式: Rust 强制显式处理错误,JavaScript 可以忽略异常

📦 Option 类型

处理可能为空的值

正在加载...

🔄 Result 类型

处理可能失败的操作

正在加载...

🎯 错误处理最佳实践

错误处理模式对比

正在加载...

高级错误处理库

在实际项目中,为了更方便地定义和处理错误,Rust 社区提供了许多优秀的错误处理库,例如:

  • thiserror: 用于简化自定义错误类型的定义,特别是当错误需要包含额外信息或从其他错误类型派生时。它通过宏自动实现 DisplayError trait。
  • anyhow: 用于简化错误传播,特别是在应用层代码中。它提供了一个简单的 Result 类型别名 anyhow::Result<T>,并允许你轻松地将各种错误类型转换为 anyhow::Error

这些库可以大大减少错误处理的样板代码,提高代码的可读性和可维护性。


🎯 练习题

练习 1: 实现 Option 方法

实现一个函数,从字符串中提取数字,返回 Option<i32>

查看答案
fn extract_number(s: &str) -> Option<i32> {
s.chars()
.filter(|c| c.is_digit(10))
.collect::<String>()
.parse::<i32>()
.ok()
}
fn main() {
println!("{:?}", extract_number("abc123def")); // Some(123)
println!("{:?}", extract_number("no numbers")); // None
println!("{:?}", extract_number("456")); // Some(456)
}

练习 2: 自定义错误类型

创建一个自定义错误类型,用于处理文件操作:

查看答案
use std::error::Error;
use std::fmt;
#[derive(Debug)]
enum FileError {
NotFound(String),
PermissionDenied(String),
InvalidFormat(String),
}
impl fmt::Display for FileError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
FileError::NotFound(path) => write!(f, "文件未找到: {}", path),
FileError::PermissionDenied(path) => write!(f, "权限被拒绝: {}", path),
FileError::InvalidFormat(msg) => write!(f, "格式无效: {}", msg),
}
}
}
impl Error for FileError {}
fn read_file(path: &str) -> Result<String, FileError> {
if path.is_empty() {
return Err(FileError::NotFound("路径为空".to_string()));
}
if path.contains("private") {
return Err(FileError::PermissionDenied(path.to_string()));
}
if path.contains("invalid") {
return Err(FileError::InvalidFormat("文件格式无效".to_string()));
}
Ok(format!("文件内容: {}", path))
}
fn main() {
match read_file("test.txt") {
Ok(content) => println!("成功: {}", content),
Err(error) => println!("错误: {}", error),
}
match read_file("") {
Ok(content) => println!("成功: {}", content),
Err(error) => println!("错误: {}", error),
}
}

练习 3: 错误传播

创建一个函数链,每个函数都可能失败,使用 ? 操作符传播错误:

查看答案
fn validate_input(input: &str) -> Result<String, String> {
if input.is_empty() {
return Err("输入不能为空".to_string());
}
Ok(input.to_string())
}
fn process_data(data: &str) -> Result<String, String> {
if data.len() < 3 {
return Err("数据太短".to_string());
}
Ok(data.to_uppercase())
}
fn save_data(data: &str) -> Result<(), String> {
if data.contains("error") {
return Err("保存失败".to_string());
}
println!("保存数据: {}", data);
Ok(())
}
fn process_pipeline(input: &str) -> Result<(), String> {
let validated = validate_input(input)?;
let processed = process_data(&validated)?;
save_data(&processed)?;
Ok(())
}
fn main() {
match process_pipeline("hello") {
Ok(_) => println!("处理成功"),
Err(error) => println!("处理失败: {}", error),
}
match process_pipeline("") {
Ok(_) => println!("处理成功"),
Err(error) => println!("处理失败: {}", error),
}
}

📝 总结

在这一章中,我们学习了 Rust 的错误处理机制:

  1. Option 类型: 处理可能为空的值
  2. Result 类型: 处理可能失败的操作
  3. 错误传播: 使用 ? 操作符传播错误
  4. 自定义错误: 创建自己的错误类型
  5. 与 JavaScript 对比: 类型安全 vs 异常处理

关键要点

  • Rust 使用类型系统而不是异常处理错误
  • OptionResult 强制显式处理错误情况
  • ? 操作符简化错误传播
  • 自定义错误类型提供更好的错误信息

下一步学习

在下一章中,我们将学习 Rust 的 Web 开发,了解如何使用 Rust 构建 Web 应用程序。


继续学习: Web 开发实战