第10章 错误处理

文章目录

第10章 错误处理

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

10.1 不可恢复错误与panic!

panic!宏的基本使用

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

rust 复制代码
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可以在多种情况下被触发:

rust 复制代码
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时的行为:

rust 复制代码
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而不是让整个程序崩溃,特别是在库代码或需要保持稳定性的场景中:

rust 复制代码
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时立即中止:

rust 复制代码
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)表示失败并包含错误信息。

rust 复制代码
#[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的方法,适应不同的使用场景:

rust 复制代码
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值:

rust 复制代码
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);
}

自定义错误类型

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

rust 复制代码
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中错误处理的重要语法糖,它可以自动传播错误,让代码更加简洁:

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)
}

?运算符的类型转换

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

rust 复制代码
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时提前返回:

rust 复制代码
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));
}

错误上下文添加

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

rust 复制代码
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 错误处理最佳实践

错误类型设计

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

rust 复制代码
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())
    }
}

分层错误处理

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

rust 复制代码
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(())
    }
}

错误恢复策略

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

rust 复制代码
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))
}

测试错误情况

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

rust 复制代码
#[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());
    }
}

日志和监控

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

rust 复制代码
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错误处理示例:

rust 复制代码
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类型系统的核心特性,能够帮助我们编写灵活且高效的代码。

相关推荐
Halo_tjn2 小时前
基于 Object 类及包装类的专项实验
java·开发语言·计算机
拾忆,想起2 小时前
超时重传 vs 快速重传:TCP双保险如何拯救网络丢包?
java·开发语言·网络·数据库·网络协议·tcp/ip·php
從南走到北2 小时前
JAVA国际版同城外卖跑腿团购到店跑腿多合一APP系统源码支持Android+IOS+H5
android·java·ios·微信小程序·小程序
花落已飘2 小时前
openEuler安全特性深度评测:构建企业级安全防护体系
安全·ai
budingxiaomoli2 小时前
多线程(一)
java·开发语言·jvm·java-ee
FunTester2 小时前
基于 Cursor 的智能测试用例生成系统 - 项目介绍与实施指南
人工智能·ai·大模型·测试用例·实践指南·curor·智能测试用例
SEO_juper2 小时前
LLMs.txt 创建指南:为大型语言模型优化您的网站
人工智能·ai·语言模型·自然语言处理·数字营销
m0_748248022 小时前
C++中的位运算符:与、或、异或详解
java·c++·算法
介一安全2 小时前
从 0 到 1 玩转 2025 最新 WebGoat 靶场:环境搭建 + 全关卡漏洞解析(超级详细)
java·web安全·网络安全·靶场