第10章 错误处理 - 指南

在这里插入图片描述

第10章 错误处理

错误处理是构建健壮、可靠软件系统的核心要素。Rust以其独特的方式处理错误,强调编译时的错误检查和显式的错误处理。与许多其他语言使用异常机制不同,Rust将错误分为可恢复错误和不可恢复错误,并通过类型系统在编译期强制开发者处理潜在的错误情况。本章将深入探讨Rust的错误处理哲学、技术实现和最佳实践。

10.1 不可恢复错误与panic!

panic!宏的基本使用

在Rust中,当程序遇到无法恢复的错误状态时,可以使用panic!宏来立即终止程序执行。panic!会产生一个不可恢复的错误,导致程序崩溃并打印错误信息。

fn basic_panic() {
println!("Before panic");
// 触发panic,程序会在此处终止
panic!("Something went terribly wrong!");
// 这行代码永远不会执行
println!("After panic");
}
fn main() {
basic_panic();
}

运行上述程序会输出类似以下的信息:

Before panic
thread 'main' panicked at 'Something went terribly wrong!', src/main.rs:5:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

panic的触发场景

panic可以在多种情况下被触发:

fn panic_scenarios() {
// 1. 显式调用panic!宏
// panic!("Explicit panic");
// 2. 数组越界访问
let arr = [1, 2, 3];
// let element = arr[10]; // 这会panic
// 3. 除零错误
// let result = 10 / 0; // 这会panic
// 4. Option::None值的unwrap()
let maybe_value: Option<i32> = None;
  // let value = maybe_value.unwrap(); // 这会panic
  // 5. Result::Err值的unwrap()
  let result: Result<i32, &str> = Err("error message");
  // let value = result.unwrap(); // 这会panic
  }

自定义panic处理

Rust允许我们自定义panic时的行为:

use std::panic;
fn custom_panic_handler() {
// 设置自定义panic hook
panic::set_hook(Box::new(|panic_info| {
println!("Custom panic handler activated!");
if let Some(location) = panic_info.location() {
println!(
"Panic occurred in file '{}' at line {}",
location.file(),
location.line()
);
}
if let Some(payload) = panic_info.payload().downcast_ref::<&str>() {
println!("Panic payload: {}", payload);
}
}));
// 现在panic会使用我们的自定义处理器
// panic!("This will use our custom handler");
}
fn main() {
custom_panic_handler();
// 取消注释下面的行来测试自定义panic处理器
// panic!("Test panic with custom handler");
}

捕获panic

在某些情况下,我们可能希望捕获panic而不是让整个程序崩溃,特别是在库代码或需要保持稳定性的场景中:

fn catch_panic_demo() {
let result = panic::catch_unwind(|| {
println!("About to panic...");
panic!("Intentional panic for demonstration");
});
match result {
Ok(_) => println!("No panic occurred"),
Err(_) => println!("Panic was caught and contained"),
}
println!("Program continues running after caught panic");
}
// 在实际应用中的例子:处理第三方库可能发生的panic
fn safe_external_call<F, R>(f: F) -> Result<R, String>
  where
  F: FnOnce() -> R + panic::UnwindSafe,
  {
  match panic::catch_unwind(f) {
  Ok(result) => Ok(result),
  Err(_) => Err("External function panicked".to_string()),
  }
  }
  fn main() {
  catch_panic_demo();
  // 测试安全的外部调用
  let result = safe_external_call(|| {
  // 模拟可能panic的第三方库调用
  if rand::random() {
  panic!("Random panic from external library");
  }
  42
  });
  match result {
  Ok(value) => println!("Success: {}", value),
  Err(e) => println!("Error: {}", e),
  }
  }

panic与栈展开

当panic发生时,Rust默认会进行栈展开(stack unwinding),这会清理每个函数调用栈帧中的数据。我们也可以配置程序在panic时立即中止:

fn stack_unwinding_demo() {
struct Resource {
name: String,
}
impl Drop for Resource {
fn drop(&mut self) {
println!("Dropping resource: {}", self.name);
}
}
let resource = Resource {
name: "Important Resource".to_string(),
};
println!("Before panic - resource exists");
// 当panic发生时,resource的drop方法会被调用
// panic!("This will trigger stack unwinding");
println!("After panic - this won't be printed");
}
// 在Cargo.toml中配置panic策略:
// [profile.release]
// panic = 'abort'  # 在release模式下panic时立即中止,不进行栈展开
fn main() {
stack_unwinding_demo();
}

10.2 Result类型与可恢复错误

Result类型基础

Result<T, E>是Rust中处理可恢复错误的主要机制。它是一个枚举,有两个变体:Ok(T)表示成功并包含结果值,Err(E)表示失败并包含错误信息。

#[derive(Debug)]
enum FileError {
NotFound,
PermissionDenied,
InvalidFormat,
}
fn read_file(path: &str) -> Result<String, FileError> {
  match path {
  "" => Err(FileError::NotFound),
  "secret.txt" => Err(FileError::PermissionDenied),
  "corrupted.data" => Err(FileError::InvalidFormat),
  _ => Ok(format!("Contents of {}", path)),
  }
  }
  fn result_basics() {
  let files = ["data.txt", "", "secret.txt", "corrupted.data"];
  for file in files.iter() {
  match read_file(file) {
  Ok(content) => println!("Success: {}", content),
  Err(FileError::NotFound) => println!("Error: File '{}' not found", file),
  Err(FileError::PermissionDenied) => println!("Error: Permission denied for '{}'", file),
  Err(FileError::InvalidFormat) => println!("Error: Invalid format for '{}'", file),
  }
  }
  }

处理Result的多种方式

Rust提供了多种处理Result的方法,适应不同的使用场景:

fn result_handling_techniques() {
let success_result: Result<i32, &str> = Ok(42);
let error_result: Result<i32, &str> = Err("Something went wrong");
// 1. 使用match表达式(最明确的方式)
match success_result {
Ok(value) => println!("Got value: {}", value),
Err(e) => println!("Got error: {}", e),
}
// 2. 使用if let
if let Ok(value) = success_result {
println!("Value is: {}", value);
}
// 3. unwrap - 获取Ok值,如果是Err则panic
let value = success_result.unwrap();
println!("Unwrapped value: {}", value);
// 4. expect - 类似unwrap,但可以指定panic消息
let value = success_result.expect("This should never fail");
println!("Expected value: {}", value);
// 5. unwrap_or - 如果是Ok返回值,如果是Err返回默认值
let value = error_result.unwrap_or(100);
println!("Value or default: {}", value);
// 6. unwrap_or_else - 类似unwrap_or,但通过闭包计算默认值
let value = error_result.unwrap_or_else(|e| {
println!("Error occurred: {}, using fallback value", e);
200
});
println!("Value or computed: {}", value);
// 7. 使用?运算符传播错误(在返回Result的函数中)
// let propagated = some_operation()?;
}

组合Result值

我们可以通过各种方法组合多个Result值:

fn combine_results() {
// and_then - 链式操作,成功时继续处理
let result1: Result<i32, &str> = Ok(5);
let combined = result1.and_then(|x| Ok(x * 2));
println!("and_then result: {:?}", combined);
// or_else - 错误时尝试恢复
let result2: Result<i32, &str> = Err("first error");
let recovered = result2.or_else(|_| Ok(100));
println!("or_else result: {:?}", recovered);
// map - 转换Ok值
let mapped = result1.map(|x| x.to_string());
println!("mapped result: {:?}", mapped);
// map_err - 转换Err值
let error_mapped = result2.map_err(|e| format!("Enhanced: {}", e));
println!("error mapped: {:?}", error_mapped);
// 处理多个Result
let results = [Ok(1), Ok(2), Err("error"), Ok(4)];
// collect可以收集Result的迭代器
let collected: Result<Vec<i32>, &str> = results.into_iter().collect();
  println!("Collected: {:?}", collected);
  // 也可以分别处理成功和失败的情况
  let (oks, errs): (Vec<_>, Vec<_>) = results
    .into_iter()
    .partition(Result::is_ok);
    println!("Oks: {:?}", oks);
    println!("Errors: {:?}", errs);
    }

自定义错误类型

在实际应用中,我们通常需要定义自己的错误类型:

use std::fmt;
use std::error::Error;
  #[derive(Debug, Clone)]
enum MathError {
DivisionByZero,
NegativeSquareRoot,
Overflow,
}
impl fmt::Display for MathError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MathError::DivisionByZero => write!(f, "Division by zero"),
MathError::NegativeSquareRoot => write!(f, "Square root of negative number"),
MathError::Overflow => write!(f, "Arithmetic overflow"),
}
}
}
impl Error for MathError {}
fn divide(a: f64, b: f64) -> Result<f64, MathError> {
  if b == 0.0 {
  Err(MathError::DivisionByZero)
  } else {
  Ok(a / b)
  }
  }
  fn square_root(x: f64) -> Result<f64, MathError> {
    if x < 0.0 {
    Err(MathError::NegativeSquareRoot)
    } else {
    Ok(x.sqrt())
    }
    }
    fn complex_operation(a: f64, b: f64) -> Result<f64, MathError> {
      // 使用?运算符传播错误
      let quotient = divide(a, b)?;
      let result = square_root(quotient)?;
      Ok(result)
      }
      fn main() {
      let test_cases = [(4.0, 2.0), (4.0, 0.0), (-4.0, 2.0)];
      for (a, b) in test_cases.iter() {
      match complex_operation(*a, *b) {
      Ok(result) => println!("√({} / {}) = {:.2}", a, b, result),
      Err(e) => println!("Error for √({} / {}): {}", a, b, e),
      }
      }
      }

10.3 ?运算符简化错误传播

?运算符基础

?运算符是Rust中错误处理的重要语法糖,它可以自动传播错误,让代码更加简洁:

use std::fs::File;
use std::io::{self, Read};
// 没有使用?运算符的版本
fn read_file_contents_manual(path: &str) -> Result<String, io::Error> {
  let mut file = match File::open(path) {
  Ok(f) => f,
  Err(e) => return Err(e),
  };
  let mut contents = String::new();
  match file.read_to_string(&mut contents) {
  Ok(_) => Ok(contents),
  Err(e) => Err(e),
  }
  }
  // 使用?运算符的版本
  fn read_file_contents_simple(path: &str) -> Result<String, io::Error> {
    let mut file = File::open(path)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents)
    }
    // 更简洁的版本
    fn read_file_contents_concise(path: &str) -> Result<String, io::Error> {
      let mut contents = String::new();
      File::open(path)?.read_to_string(&mut contents)?;
      Ok(contents)
      }

?运算符的类型转换

?运算符可以自动进行错误类型转换,这在使用多个错误类型时特别有用:

use std::num::ParseIntError;
  #[derive(Debug)]
enum MyError {
Io(io::Error),
Parse(ParseIntError),
Custom(String),
}
impl From<io::Error> for MyError {
  fn from(error: io::Error) -> Self {
  MyError::Io(error)
  }
  }
  impl From<ParseIntError> for MyError {
    fn from(error: ParseIntError) -> Self {
    MyError::Parse(error)
    }
    }
    fn read_and_parse_file(path: &str) -> Result<i32, MyError> {
      // ?运算符会自动调用From trait进行类型转换
      let contents = std::fs::read_to_string(path)?;
      let number: i32 = contents.trim().parse()?;
      Ok(number)
      }
      fn main() {
      match read_and_parse_file("number.txt") {
      Ok(num) => println!("Parsed number: {}", num),
      Err(MyError::Io(e)) => println!("IO error: {}", e),
      Err(MyError::Parse(e)) => println!("Parse error: {}", e),
      Err(MyError::Custom(msg)) => println!("Custom error: {}", msg),
      }
      }

在Option中使用?

?运算符也可以用于Option<T>类型,在遇到None时提前返回:

fn find_username(user_id: u32) -> Option<String> {
  if user_id == 1 {
  Some("alice".to_string())
  } else {
  None
  }
  }
  fn find_email(username: &str) -> Option<String> {
    if username == "alice" {
    Some("alice@example.com".to_string())
    } else {
    None
    }
    }
    // 使用模式匹配的版本
    fn get_user_email_manual(user_id: u32) -> Option<String> {
      match find_username(user_id) {
      Some(username) => match find_email(&username) {
      Some(email) => Some(email),
      None => None,
      },
      None => None,
      }
      }
      // 使用?运算符的版本
      fn get_user_email_simple(user_id: u32) -> Option<String> {
        let username = find_username(user_id)?;
        let email = find_email(&username)?;
        Some(email)
        }
        fn main() {
        println!("Email for user 1: {:?}", get_user_email_simple(1));
        println!("Email for user 2: {:?}", get_user_email_simple(2));
        }

错误上下文添加

在实际应用中,我们经常需要为错误添加上下文信息:

use std::error::Error;
use std::fmt;
  #[derive(Debug)]
struct ContextError<E> {
  context: String,
  source: E,
  }
  impl<E: Error> Error for ContextError<E> {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
    Some(&self.source)
    }
    }
    impl<E: fmt::Display> fmt::Display for ContextError<E> {
      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
      write!(f, "{}: {}", self.context, self.source)
      }
      }
      trait ResultExt<T, E> {
        fn context<C>(self, context: C) -> Result<T, ContextError<E>>
          where
          C: Into<String>;
            }
            impl<T, E> ResultExt<T, E> for Result<T, E> {
              fn context<C>(self, context: C) -> Result<T, ContextError<E>>
                where
                C: Into<String>,
                  {
                  self.map_err(|e| ContextError {
                  context: context.into(),
                  source: e,
                  })
                  }
                  }
                  fn read_config_file() -> Result<String, Box<dyn Error>> {
                    let path = "config.toml";
                    let content = std::fs::read_to_string(path)
                    .context(format!("Failed to read config file: {}", path))?;
                    // 模拟配置解析
                    if content.is_empty() {
                    return Err("Empty config file".into());
                    }
                    Ok(content)
                    }
                    fn main() {
                    match read_config_file() {
                    Ok(config) => println!("Config: {}", config),
                    Err(e) => println!("Error: {}", e),
                    }
                    }

10.4 错误处理最佳实践

错误类型设计

设计良好的错误类型是构建健壮系统的关键:

use std::fmt;
use std::error::Error as StdError;
  #[derive(Debug)]
pub enum DatabaseError {
ConnectionFailed(String),
QueryFailed { sql: String, err: String },
TransactionFailed,
Timeout,
}
impl fmt::Display for DatabaseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
DatabaseError::ConnectionFailed(msg) => {
write!(f, "Database connection failed: {}", msg)
}
DatabaseError::QueryFailed { sql, err } => {
write!(f, "Query failed: '{}'. Error: {}", sql, err)
}
DatabaseError::TransactionFailed => {
write!(f, "Database transaction failed")
}
DatabaseError::Timeout => {
write!(f, "Database operation timed out")
}
}
}
}
impl StdError for DatabaseError {
fn source(&self) -> Option<&(dyn StdError + 'static)> {
None
}
}
// 错误转换
impl From<&str> for DatabaseError {
fn from(err: &str) -> Self {
DatabaseError::ConnectionFailed(err.to_string())
}
}

分层错误处理

在大型应用中,采用分层的错误处理策略:

mod application {
use super::DatabaseError;
  #[derive(Debug)]
pub enum AppError {
Database(DatabaseError),
Config(String),
Auth(String),
BusinessLogic(String),
}
impl From<DatabaseError> for AppError {
  fn from(err: DatabaseError) -> Self {
  AppError::Database(err)
  }
  }
  impl std::fmt::Display for AppError {
  fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
  match self {
  AppError::Database(e) => write!(f, "Database error: {}", e),
  AppError::Config(msg) => write!(f, "Configuration error: {}", msg),
  AppError::Auth(msg) => write!(f, "Authentication error: {}", msg),
  AppError::BusinessLogic(msg) => write!(f, "Business logic error: {}", msg),
  }
  }
  }
  impl std::error::Error for AppError {}
  }
  // 使用示例
  use application::AppError;
  fn process_user_request(user_id: u32) -> Result<String, AppError> {
    // 各种可能失败的操作
    validate_user(user_id)?;
    load_user_data(user_id)?;
    apply_business_rules(user_id)?;
    Ok("Request processed successfully".to_string())
    }
    fn validate_user(_user_id: u32) -> Result<(), AppError> {
      // 模拟验证逻辑
      if rand::random() {
      Err(AppError::Auth("Invalid user credentials".to_string()))
      } else {
      Ok(())
      }
      }
      fn load_user_data(_user_id: u32) -> Result<(), AppError> {
        // 模拟数据库操作
        if rand::random() {
        Err(DatabaseError::ConnectionFailed("Connection timeout".to_string()).into())
        } else {
        Ok(())
        }
        }
        fn apply_business_rules(_user_id: u32) -> Result<(), AppError> {
          // 模拟业务逻辑
          if rand::random() {
          Err(AppError::BusinessLogic("Invalid operation".to_string()))
          } else {
          Ok(())
          }
          }

错误恢复策略

根据错误的性质采取不同的恢复策略:

use std::time::Duration;
use std::thread;
  #[derive(Debug)]
enum NetworkError {
Timeout,
ConnectionRefused,
InvalidData,
}
fn fetch_data_with_retry(url: &str, max_retries: u32) -> Result<String, NetworkError> {
  for attempt in 1..=max_retries {
  println!("Attempt {} to fetch {}", attempt, url);
  match fetch_data(url) {
  Ok(data) => return Ok(data),
  Err(NetworkError::Timeout) => {
  println!("Timeout occurred, retrying...");
  if attempt < max_retries {
  thread::sleep(Duration::from_secs(1 << attempt)); // 指数退避
  }
  }
  Err(e) => return Err(e), // 非重试错误立即返回
  }
  }
  Err(NetworkError::Timeout)
  }
  fn fetch_data(_url: &str) -> Result<String, NetworkError> {
    // 模拟网络请求
    match rand::random_range(0..3) {
    0 => Ok("Data received".to_string()),
    1 => Err(NetworkError::Timeout),
    2 => Err(NetworkError::ConnectionRefused),
    _ => Err(NetworkError::InvalidData),
    }
    }
    // 回退策略
    fn get_data_with_fallback(primary_url: &str, fallback_url: &str) -> Result<String, String> {
      fetch_data_with_retry(primary_url, 3)
      .or_else(|_| {
      println!("Primary failed, trying fallback...");
      fetch_data_with_retry(fallback_url, 3)
      })
      .map_err(|e| format!("All attempts failed: {:?}", e))
      }

测试错误情况

确保测试覆盖各种错误路径:

#[cfg(test)]
mod tests {
use super::*;
  #[test]
fn test_divide_by_zero() {
assert!(divide(10.0, 0.0).is_err());
}
  #[test]
fn test_negative_square_root() {
assert!(square_root(-1.0).is_err());
}
  #[test]
fn test_valid_operations() {
assert_eq!(divide(10.0, 2.0).unwrap(), 5.0);
assert_eq!(square_root(9.0).unwrap(), 3.0);
}
  #[test]
  #[should_panic(expected = "Division by zero")]
fn test_panic_on_unwrap() {
divide(10.0, 0.0).unwrap();
}
  #[test]
fn test_error_propagation() {
let result = complex_operation(16.0, 4.0);
assert!(result.is_ok());
assert_eq!(result.unwrap(), 2.0);
let result = complex_operation(16.0, 0.0);
assert!(result.is_err());
}
}

日志和监控

在生产环境中,适当的日志记录和监控至关重要:

use std::error::Error;
fn process_with_logging() -> Result<(), Box<dyn Error>> {
  // 模拟一些可能失败的操作
  let steps = ["connect", "authenticate", "process", "disconnect"];
  for step in steps.iter() {
  println!("[INFO] Starting step: {}", step);
  let result = match *step {
  "connect" => simulate_operation(0.8),
  "authenticate" => simulate_operation(0.9),
  "process" => simulate_operation(0.7),
  "disconnect" => simulate_operation(0.95),
  _ => Ok(()),
  };
  match result {
  Ok(()) => println!("[INFO] Step {} completed successfully", step),
  Err(e) => {
  println!("[ERROR] Step {} failed: {}", step, e);
  return Err(e);
  }
  }
  }
  println!("[INFO] All steps completed successfully");
  Ok(())
  }
  fn simulate_operation(success_rate: f64) -> Result<(), String> {
    if rand::random::<f64>() < success_rate {
      Ok(())
      } else {
      Err("Operation failed randomly".to_string())
      }
      }
      // 错误指标收集
      struct ErrorMetrics {
      total_errors: u32,
      error_types: std::collections::HashMap<String, u32>,
        }
        impl ErrorMetrics {
        fn new() -> Self {
        ErrorMetrics {
        total_errors: 0,
        error_types: std::collections::HashMap::new(),
        }
        }
        fn record_error<E: Error>(&mut self, error: &E) {
          self.total_errors += 1;
          let error_type = format!("{}", error);
          *self.error_types.entry(error_type).or_insert(0) += 1;
          }
          fn report(&self) {
          println!("Total errors: {}", self.total_errors);
          println!("Error breakdown:");
          for (error_type, count) in &self.error_types {
          println!("  {}: {}", error_type, count);
          }
          }
          }

实战:完整的API错误处理

让我们构建一个完整的API错误处理示例:

use std::collections::HashMap;
use std::fmt;
  #[derive(Debug, Clone)]
pub struct ApiError {
pub code: u16,
pub message: String,
pub details: Option<HashMap<String, String>>,
  }
  impl ApiError {
  pub fn new(code: u16, message: &str) -> Self {
  ApiError {
  code,
  message: message.to_string(),
  details: None,
  }
  }
  pub fn with_details(mut self, details: HashMap<String, String>) -> Self {
    self.details = Some(details);
    self
    }
    pub fn bad_request(message: &str) -> Self {
    Self::new(400, message)
    }
    pub fn unauthorized(message: &str) -> Self {
    Self::new(401, message)
    }
    pub fn not_found(message: &str) -> Self {
    Self::new(404, message)
    }
    pub fn internal_error(message: &str) -> Self {
    Self::new(500, message)
    }
    }
    impl fmt::Display for ApiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
    write!(f, "API Error {}: {}", self.code, self.message)
    }
    }
    impl std::error::Error for ApiError {}
    // API响应类型
    pub type ApiResult<T> = Result<T, ApiError>;
      // 用户服务
      struct UserService;
      impl UserService {
      fn get_user(&self, user_id: u32) -> ApiResult<User> {
        if user_id == 0 {
        return Err(ApiError::bad_request("Invalid user ID"));
        }
        if user_id > 1000 {
        return Err(ApiError::not_found("User not found"));
        }
        Ok(User {
        id: user_id,
        name: format!("User {}", user_id),
        email: format!("user{}@example.com", user_id),
        })
        }
        fn create_user(&self, user_data: CreateUserRequest) -> ApiResult<User> {
          if user_data.name.is_empty() {
          let mut details = HashMap::new();
          details.insert("field".to_string(), "name".to_string());
          details.insert("reason".to_string(), "cannot be empty".to_string());
          return Err(ApiError::bad_request("Invalid user data")
          .with_details(details));
          }
          if !user_data.email.contains('@') {
          let mut details = HashMap::new();
          details.insert("field".to_string(), "email".to_string());
          details.insert("reason".to_string(), "must contain @".to_string());
          return Err(ApiError::bad_request("Invalid email format")
          .with_details(details));
          }
          Ok(User {
          id: rand::random_range(1..1000),
          name: user_data.name,
          email: user_data.email,
          })
          }
          }
          // 数据模型
            #[derive(Debug)]
          struct User {
          id: u32,
          name: String,
          email: String,
          }
          struct CreateUserRequest {
          name: String,
          email: String,
          }
          // API处理器
          struct ApiHandler {
          user_service: UserService,
          }
          impl ApiHandler {
          fn new() -> Self {
          ApiHandler {
          user_service: UserService,
          }
          }
          fn handle_get_user(&self, user_id: u32) -> ApiResult<String> {
            let user = self.user_service.get_user(user_id)?;
            Ok(format!("User: {} ({})", user.name, user.email))
            }
            fn handle_create_user(&self, name: String, email: String) -> ApiResult<String> {
              let request = CreateUserRequest { name, email };
              let user = self.user_service.create_user(request)?;
              Ok(format!("Created user: {} with ID {}", user.name, user.id))
              }
              }
              // 错误处理中间件
              trait ErrorHandler {
              fn handle_error(&self, error: &ApiError) -> String;
              }
              struct JsonErrorHandler;
              impl ErrorHandler for JsonErrorHandler {
              fn handle_error(&self, error: &ApiError) -> String {
              let mut response = HashMap::new();
              response.insert("error".to_string(), true);
              response.insert("code".to_string(), error.code.to_string());
              response.insert("message".to_string(), error.message.clone());
              if let Some(details) = &error.details {
              response.insert("details".to_string(), serde_json::to_string(details).unwrap());
              }
              serde_json::to_string(&response).unwrap()
              }
              }
              fn main() {
              let api = ApiHandler::new();
              let error_handler = JsonErrorHandler;
              // 测试各种场景
              let test_cases = vec![
              (1, "alice", "alice@example.com"),
              (0, "", "invalid-email"),
              (2000, "bob", "bob@example.com"),
              ];
              for (user_id, name, email) in test_cases {
              println!("\n=== Processing user ID: {} ===", user_id);
              // 获取用户
              match api.handle_get_user(user_id) {
              Ok(response) => println!("Success: {}", response),
              Err(error) => println!("Error: {}", error_handler.handle_error(&error)),
              }
              // 创建用户
              match api.handle_create_user(name.to_string(), email.to_string()) {
              Ok(response) => println!("Success: {}", response),
              Err(error) => println!("Error: {}", error_handler.handle_error(&error)),
              }
              }
              }

总结

Rust的错误处理系统是其可靠性和安全性的重要组成部分。通过本章的学习,我们掌握了:

  1. 不可恢复错误(panic):用于处理程序无法恢复的严重错误
  2. 可恢复错误(Result):通过类型系统强制处理可能的错误情况
  3. 错误传播:使用?运算符简化错误传播代码
  4. 错误处理最佳实践:包括错误类型设计、分层处理、恢复策略等

Rust的错误处理哲学强调显式和编译期检查,这虽然在开始时需要更多代码,但最终会带来更健壮、更可维护的软件系统。通过合理运用这些技术,你可以构建出既安全又易于调试的Rust应用程序。

在下一章中,我们将探讨Rust的泛型、trait和生命周期系统,这些是Rust类型系统的核心特性,能够帮助我们编写灵活且高效的代码。

posted @ 2025-12-05 11:19  yangykaifa  阅读(2)  评论(0)    收藏  举报