Rust错误处理机制:Result与Option实战解析

1. Rust 错误处理机制深度解析

作为一门系统级编程语言,Rust 的错误处理机制是其最引人注目的特性之一。与传统的异常处理机制不同,Rust 采用了基于返回值的显式错误处理方式,这种设计在保证安全性的同时,也带来了独特的编程体验。

1.1 Result 与 Option 类型

Rust 主要通过两种枚举类型来处理可能出现的错误情况:

enum Option<T> { Some(T), None, } enum Result<T, E> { Ok(T), Err(E), }

Option 用于表示值可能存在或不存在的情况,而 Result 则专门用于可能失败的操作。这种设计强制开发者必须显式处理所有可能的错误路径,避免了传统异常处理中可能出现的"忘记捕获"问题。

在实际编码中,我们经常会遇到这样的模式:

fn divide(a: f64, b: f64) -> Result<f64, String> { if b == 0.0 { Err(String::from("Cannot divide by zero")) } else { Ok(a / b) } }

提示:在 Rust 中,错误类型可以是任何实现了 std::error::Error trait 的类型,但通常建议使用专门的错误类型库(如 thiserror、anyhow)来创建更丰富的错误类型。

1.2 错误传播与 ? 运算符

Rust 提供了简洁的错误传播语法糖 - ? 运算符。这个运算符会自动解包 Result,如果是 Ok 则继续执行,如果是 Err 则提前从当前函数返回错误。

fn process_file(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) }

这种写法比传统的 match 表达式更加简洁,同时保持了相同的安全性。值得注意的是,? 运算符会自动进行错误类型的转换,前提是相关错误类型实现了 From trait。

2. 错误处理最佳实践

2.1 自定义错误类型

对于复杂的项目,定义自己的错误类型是必要的。Rust 社区有几个流行的方案:

  1. thiserror:适合需要定义结构化错误类型的库
  2. anyhow:适合应用程序级的错误处理
  3. snafu:提供更强大的上下文错误处理能力

以 thiserror 为例:

#[derive(Debug, thiserror::Error)] enum MyError { #[error("IO error: {0}")] Io(#[from] io::Error), #[error("Parse error: {0}")] Parse(String), #[error("Network error: {0}")] Network(#[from] reqwest::Error), }

2.2 错误处理模式

在实际开发中,我们通常会遇到以下几种错误处理模式:

  1. 立即处理:在错误发生的地方直接处理
  2. 传播处理:将错误传递给调用者处理
  3. 转换处理:将底层错误转换为更高级别的错误类型
  4. 组合处理:将多个可能错误的操作组合起来处理

一个常见的组合处理例子:

fn process_data() -> Result<(), MyError> { let data = read_config() .and_then(|config| fetch_data(&config.url)) .and_then(|raw_data| parse_data(&raw_data))?; analyze_data(&data)?; Ok(()) }

3. 高级错误处理技巧

3.1 错误链与上下文

在复杂的应用中,为错误添加上下文信息非常重要。Rust 提供了多种方式来实现这一点:

use anyhow::{Context, Result}; fn load_config(path: &str) -> Result<Config> { let config = std::fs::read_to_string(path) .with_context(|| format!("Failed to read config at {}", path))?; toml::from_str(&config) .context("Failed to parse config file") }

3.2 错误恢复策略

根据应用场景不同,我们可以采用不同的错误恢复策略:

  1. 重试策略:对于暂时性错误(如网络问题)
  2. 回退策略:提供备选方案
  3. 降级策略:在部分功能不可用时提供简化服务
  4. 熔断策略:防止错误扩散

一个简单的重试实现:

fn retry<F, T, E>(mut f: F, max_retries: usize) -> Result<T, E> where F: FnMut() -> Result<T, E>, { let mut last_error = None; for _ in 0..max_retries { match f() { Ok(val) => return Ok(val), Err(e) => last_error = Some(e), } std::thread::sleep(std::time::Duration::from_secs(1)); } Err(last_error.unwrap()) }

4. 测试中的错误处理

4.1 测试预期错误

在单元测试中,我们经常需要验证函数是否按预期返回错误:

#[test] fn test_divide_by_zero() { assert!(divide(1.0, 0.0).is_err()); assert_eq!( divide(1.0, 0.0).unwrap_err(), "Cannot divide by zero" ); }

4.2 集成测试中的错误处理

对于集成测试,Rust 的测试框架提供了更丰富的功能:

#[test] fn test_file_processing() -> Result<(), Box<dyn std::error::Error>> { let temp_file = create_test_file()?; let result = process_file(temp_file.path().to_str().unwrap())?; assert!(!result.is_empty()); Ok(()) }

注意:在测试中使用 ? 运算符时,测试函数的返回类型必须是 Result 类型,这样测试框架才能正确处理可能的错误。

5. 性能考量与优化

5.1 错误处理的开销

Rust 的错误处理机制在性能上有几个特点:

  1. 无堆分配:基本的 Result 和 Option 不涉及堆分配
  2. 零成本抽象:match 和 ? 运算符在优化后几乎没有额外开销
  3. 错误路径优化:编译器会对错误路径进行特殊优化

5.2 热点路径优化

在性能关键代码中,我们可以采用一些优化技巧:

  1. 使用 Option 代替 Result 当不需要错误信息时
  2. 避免在热点路径中构造复杂的错误类型
  3. 使用 unwrap 或 expect 在确定不会出错的地方(但要谨慎)
// 在性能关键且确定不会出错的代码段 let value = unsafe { unsafe_op().unwrap_unchecked() };

6. 与其他语言的互操作

6.1 与C的交互

当与C代码交互时,需要特别注意错误处理的转换:

extern "C" { fn c_function() -> libc::c_int; } fn call_c_function() -> Result<(), &'static str> { let result = unsafe { c_function() }; if result == 0 { Ok(()) } else { Err("C function failed") } }

6.2 与异常语言的交互

与抛出异常的语言(如C++、Python)交互时,需要使用特殊的包装:

#[no_mangle] pub extern "C" fn safe_wrapper() -> *mut std::os::raw::c_char { let result = std::panic::catch_unwind(|| { // 可能抛出异常的代码 }); match result { Ok(_) => std::ptr::null_mut(), Err(_) => CString::new("Error occurred").unwrap().into_raw(), } }

7. 异步环境中的错误处理

7.1 Future 和 Error

在异步编程中,错误处理有一些特殊考虑:

async fn async_operation() -> Result<Data, Error> { let data = fetch_data().await?; process_data(data).await }

7.2 组合异步错误

使用 futures 库可以方便地组合多个可能失败的异步操作:

use futures::future::try_join_all; async fn process_multiple(items: Vec<Item>) -> Result<Vec<Output>, Error> { let futures = items.into_iter().map(|item| process_item(item)); try_join_all(futures).await }

8. 工具链与生态系统

8.1 常用错误处理库

  1. thiserror:用于定义结构化错误类型
  2. anyhow:简化应用程序的错误处理
  3. snafu:提供错误上下文和回溯
  4. eyre:类似于 anyhow,但支持自定义报告格式

8.2 调试工具

Rust 提供了多种调试错误处理的工具:

  1. RUST_BACKTRACE=1:获取详细的错误回溯
  2. color-eyre:带颜色标记的错误报告
  3. tracing:分布式追踪错误路径

一个配置示例:

use color_eyre::eyre::{self, Report}; fn main() -> Result<(), Report> { color_eyre::install()?; // 应用代码 Ok(()) }

9. 实际项目中的错误处理架构

9.1 分层错误处理

在大型项目中,通常会采用分层的错误处理策略:

  1. 基础设施层:处理原始IO、网络等底层错误
  2. 领域层:定义业务相关的错误类型
  3. 应用层:处理用户交互和错误展示

9.2 错误转换策略

在不同层之间传递错误时,通常会进行错误转换:

mod domain { #[derive(Debug, thiserror::Error)] pub enum Error { #[error("Invalid input: {0}")] Validation(String), #[error("Internal error")] Internal(#[from] anyhow::Error), } } mod infrastructure { pub fn db_operation() -> anyhow::Result<()> { // ... } } fn business_logic() -> Result<(), domain::Error> { infrastructure::db_operation()?; Ok(()) }

10. 错误处理的可观测性

10.1 日志记录

良好的错误处理应该包含足够的日志信息:

use tracing::{error, info}; fn process_item(item: Item) -> Result<(), Error> { info!("Processing item {}", item.id); match do_work(&item) { Ok(_) => { info!("Successfully processed item {}", item.id); Ok(()) } Err(e) => { error!("Failed to process item {}: {:?}", item.id, e); Err(e) } } }

10.2 指标监控

使用指标来跟踪错误率:

use metrics::{counter, histogram}; fn endpoint_handler() -> Result<Response, Error> { let timer = histogram!("handler.duration").start(); let result = handle_request(); match &result { Ok(_) => counter!("handler.success").increment(1), Err(_) => counter!("handler.errors").increment(1), } timer.observe_duration(); result }

11. 跨线程错误传递

11.1 线程间错误传递

在多线程环境中传递错误需要特别注意:

use std::thread; fn parallel_processing() -> Result<(), Box<dyn std::error::Error>> { let handle = thread::spawn(|| { // 可能失败的操作 heavy_computation() }); match handle.join() { Ok(res) => res.map_err(|e| e.into()), Err(e) => Err(format!("Thread panicked: {:?}", e).into()), } }

11.2 通道中的错误处理

使用通道传递结果和错误:

use std::sync::mpsc; fn parallel_task() -> Result<Data, Error> { let (tx, rx) = mpsc::channel(); thread::spawn(move || { let result = compute(); tx.send(result).unwrap(); }); rx.recv().unwrap() }

12. FFI 边界错误处理

12.1 向其他语言暴露API

当 Rust 代码被其他语言调用时,需要特殊的错误处理方式:

#[no_mangle] pub extern "C" fn rust_function(arg: *const c_char) -> *mut c_char { let result = panic::catch_unwind(|| { let arg_str = unsafe { CStr::from_ptr(arg) }.to_str()?; let output = process(arg_str)?; CString::new(output).map_err(|_| "String conversion failed") }); match result { Ok(Ok(cstr)) => cstr.into_raw(), Ok(Err(e)) => CString::new(e).unwrap().into_raw(), Err(_) => CString::new("panic occurred").unwrap().into_raw(), } }

12.2 错误码模式

对于 C 兼容的 API,通常使用错误码模式:

#[repr(C)] pub enum ErrorCode { Success = 0, InvalidArgument = 1, IoError = 2, // ... } #[no_mangle] pub extern "C" fn operation(arg: i32) -> ErrorCode { match try_operation(arg) { Ok(_) => ErrorCode::Success, Err(e) => e.into(), } }

13. 宏与错误处理

13.1 简化错误处理的宏

可以创建宏来简化重复的错误处理模式:

macro_rules! try_log { ($expr:expr) => { match $expr { Ok(val) => val, Err(e) => { log::error!("Error at {}:{} - {}", file!(), line!(), e); return Err(e.into()); } } }; } fn process() -> Result<(), Error> { let data = try_log!(load_data()); let result = try_log!(compute(&data)); try_log!(save_result(result)); Ok(()) }

13.2 错误生成宏

创建统一的错误生成方式:

macro_rules! err { ($($arg:tt)*) => { Err(format!($($arg)*).into()) }; } fn validate(input: &str) -> Result<(), Error> { if input.is_empty() { err!("Input cannot be empty")? } Ok(()) }

14. 无恐慌(Panic-Free)编程

14.1 避免恐慌的策略

在某些关键系统中,需要确保代码不会恐慌:

  1. 使用 Option/Result 代替 unwrap
  2. 边界检查代替直接索引
  3. 使用 checked/saturating/wrapping 算术运算
  4. 避免断言(assert)
fn safe_divide(a: i32, b: i32) -> Option<i32> { if b == 0 { None } else { Some(a / b) } }

14.2 恐慌边界

即使大部分代码不恐慌,也需要定义清晰的恐慌边界:

fn main() { if let Err(e) = panic::catch_unwind(|| { if let Err(e) = run_application() { eprintln!("Application error: {}", e); std::process::exit(1); } }) { eprintln!("Critical error: {:?}", e); std::process::exit(2); } }

15. 错误处理模式比较

15.1 Rust 与其他语言对比

特性RustJava/C#GoC++
主要机制Result/Option异常多返回值异常/错误码
性能影响极小较大可变
显式处理强制可选部分可选
堆栈展开
错误类型系统丰富丰富简单可变

15.2 不同场景下的选择

  1. 库开发:使用 thiserror 定义精确的错误类型
  2. 应用开发:anyhow/eyre 简化错误处理
  3. 性能关键代码:最小化错误处理开销
  4. 原型开发:更多使用 unwrap/expect 加速开发

16. 错误处理与类型系统

16.1 利用类型系统减少错误

Rust 的类型系统可以帮助我们在编译期避免许多错误:

struct NonEmptyString(String); impl NonEmptyString { fn new(s: String) -> Option<Self> { if s.is_empty() { None } else { Some(Self(s)) } } } fn process(s: NonEmptyString) { // 不需要再检查字符串是否为空 }

16.2 状态机模式

使用类型系统确保正确的操作顺序:

struct Initial; struct Configured; struct Running; struct Connection<S> { // 私有字段 _state: S, } impl Connection<Initial> { fn new() -> Self { Self { _state: Initial } } fn configure(self) -> Connection<Configured> { Connection { _state: Configured } } } impl Connection<Configured> { fn start(self) -> Connection<Running> { Connection { _state: Running } } } impl Connection<Running> { fn send(&mut self, data: &[u8]) -> Result<(), Error> { // 实现发送逻辑 Ok(()) } }

17. 错误处理与并发模式

17.1 工作窃取模式中的错误处理

在使用工作窃取模式时,错误处理需要特别设计:

use crossbeam::deque; fn worker_loop(worker: deque::Worker<Task>) -> Result<(), Error> { loop { match worker.steal() { deque::Steal::Success(task) => { if let Err(e) = task.execute() { error!("Task failed: {}", e); // 决定是继续还是终止 } } deque::Steal::Empty => break, deque::Steal::Retry => continue, } } Ok(()) }

17.2 Actor 模式中的错误处理

在 Actor 模型中,错误通常通过消息传递:

struct MyActor { receiver: mpsc::Receiver<ActorMessage>, sender: mpsc::Sender<Result<ActorResponse, ActorError>>, } impl MyActor { fn run(mut self) { while let Ok(msg) = self.receiver.recv() { let result = match msg { ActorMessage::DoWork => self.do_work(), ActorMessage::Shutdown => break, }; if let Err(e) = self.sender.send(result) { error!("Failed to send response: {}", e); } } } }

18. 领域特定错误处理

18.1 Web 开发中的错误处理

在 Web 框架如 Actix-web 中:

use actix_web::{error, HttpResponse}; #[derive(Debug, thiserror::Error)] enum ApiError { #[error("Not Found")] NotFound, #[error("Internal Server Error")] Internal, } impl error::ResponseError for ApiError { fn error_response(&self) -> HttpResponse { match self { ApiError::NotFound => HttpResponse::NotFound().json("Not Found"), ApiError::Internal => HttpResponse::InternalServerError().json("Internal Error"), } } } async fn get_item(id: web::Path<u32>) -> Result<Json<Item>, ApiError> { let item = find_item(*id).ok_or(ApiError::NotFound)?; Ok(Json(item)) }

18.2 游戏开发中的错误处理

在游戏循环中通常需要不同的策略:

fn game_loop() { let mut game = Game::new(); while game.running() { if let Err(e) = game.update() { match e { GameError::Recoverable => { log_error(e); continue; } GameError::Fatal => { log_error(e); break; } } } if let Err(e) = game.render() { log_error(e); // 渲染错误通常不影响游戏逻辑 } } }

19. 错误处理与模式匹配

19.1 高级模式匹配技巧

Rust 的模式匹配可以非常精细地处理错误:

match result { Ok(Data::A { field1, .. }) if field1 > 10 => process_a(field1), Ok(Data::B { field2 }) => process_b(field2), Err(Error::Io(e)) if e.kind() == io::ErrorKind::NotFound => handle_missing_file(), Err(Error::Io(e)) => handle_io_error(e), Err(Error::Parse(e)) => handle_parse_error(e), _ => default_handler(), }

19.2 错误转换模式

使用模式匹配进行错误转换:

fn convert_error(e: Error) -> ApiError { match e { Error::NotFound => ApiError::NotFound, Error::PermissionDenied => ApiError::Forbidden, Error::Timeout => ApiError::ServiceUnavailable, _ => ApiError::Internal, } }

20. 错误处理与生命周期

20.1 错误中的生命周期

当错误包含引用时,需要正确处理生命周期:

#[derive(Debug, thiserror::Error)] enum ParseError<'a> { #[error("Invalid character at position {pos}: {input}")] InvalidChar { pos: usize, input: &'a str }, } fn parse<'a>(input: &'a str) -> Result<(), ParseError<'a>> { for (i, c) in input.chars().enumerate() { if !c.is_ascii() { return Err(ParseError::InvalidChar { pos: i, input }); } } Ok(()) }

20.2 错误与所有权

有时错误需要取得所有权:

#[derive(Debug, thiserror::Error)] enum ProcessingError { #[error("Invalid data: {0}")] InvalidData(String), // 取得字符串所有权 #[error("IO error")] Io(#[from] io::Error), } fn process_data(data: String) -> Result<(), ProcessingError> { if data.is_empty() { Err(ProcessingError::InvalidData(data)) // 转移所有权 } else { Ok(()) } }

21. 错误处理与泛型

21.1 泛型错误类型

编写泛型代码时,错误处理也需要考虑泛型:

trait Processor<T> { type Error; fn process(&mut self, item: T) -> Result<(), Self::Error>; } struct MyProcessor; impl Processor<i32> for MyProcessor { type Error = MathError; fn process(&mut self, item: i32) -> Result<(), MathError> { if item < 0 { Err(MathError::Negative) } else { Ok(()) } } }

21.2 错误类型约束

对泛型错误类型添加约束:

fn process_all<E, T>(items: T) -> Result<(), E> where T: IntoIterator, T::Item: Processable<Error = E>, E: std::error::Error, { for item in items { item.process()?; } Ok(()) }

22. 错误处理与特征对象

22.1 使用特征对象统一错误类型

当需要处理多种错误类型时,可以使用特征对象:

fn handle_errors(result: Result<(), Box<dyn std::error::Error>>) { match result { Ok(_) => println!("Success"), Err(e) => println!("Error: {}", e), } }

22.2 自定义特征对象错误

创建更具体的特征对象错误:

trait DatabaseError: std::error::Error { fn is_retryable(&self) -> bool; } fn execute_query() -> Result<(), Box<dyn DatabaseError>> { // ... }

23. 错误处理与序列化

23.1 错误序列化

当需要将错误序列化时:

#[derive(Debug, Serialize, thiserror::Error)] enum ApiError { #[error("Not Found")] NotFound, #[error("Validation Error: {0}")] Validation(String), } fn to_json(e: &ApiError) -> String { serde_json::to_string(e).unwrap() }

23.2 反序列化错误

处理反序列化时的错误:

fn parse_config(json: &str) -> Result<Config, ConfigError> { serde_json::from_str(json).map_err(|e| match e.classify() { serde_json::error::Category::Io => ConfigError::Io, serde_json::error::Category::Syntax => ConfigError::Syntax, serde_json::error::Category::Data => ConfigError::InvalidData, serde_json::error::Category::Eof => ConfigError::UnexpectedEof, }) }

24. 错误处理与日志集成

24.1 结构化日志记录

将错误记录为结构化日志:

use tracing_error::ErrorLayer; use tracing_subscriber::prelude::*; fn init_logging() { tracing_subscriber::registry() .with(ErrorLayer::default()) .init(); } fn process() -> Result<(), Error> { let span = tracing::info_span!("processing"); let _enter = span.enter(); step_one().map_err(|e| { tracing::error!(error = %e, "Step one failed"); e })?; Ok(()) }

24.2 错误上下文记录

使用错误上下文增强日志:

fn load_config(path: &str) -> Result<Config, Error> { let file = File::open(path) .with_context(|| format!("Failed to open config file at {}", path))?; let config = serde_json::from_reader(file) .context("Failed to parse config file")?; Ok(config) }

25. 错误处理与测试策略

25.1 测试错误路径

确保测试覆盖错误路径:

#[test] fn test_error_conditions() { assert!(divide(1.0, 0.0).is_err()); assert_matches!(parse("invalid"), Err(ParseError::InvalidFormat)); let err = process_file("nonexistent.txt").unwrap_err(); assert_eq!(err.to_string(), "File not found"); }

25.2 模糊测试中的错误处理

在模糊测试中验证错误处理:

#[cfg(test)] mod fuzz { use arbitrary::Arbitrary; #[derive(Debug, Arbitrary)] struct Input { a: i32, b: i32, } #[test] fn test_divide() { let input = Input::arbitrary(); if input.b == 0 { assert!(divide(input.a, input.b).is_err()); } else { assert!(divide(input.a, input.b).is_ok()); } } }

26. 错误处理与文档

26.1 文档中的错误说明

在文档注释中明确错误情况:

/// Divides two numbers. /// /// # Examples /// ``` /// assert_eq!(divide(10, 2), Ok(5)); /// ``` /// /// # Errors /// Returns `Err` if: /// - The divisor is zero /// - The result would overflow pub fn divide(a: i32, b: i32) -> Result<i32, MathError> { // ... }

26.2 错误代码文档

为错误代码创建详细文档:

#[derive(Debug, thiserror::Error)] #[error("Database Error")] pub enum DbError { /// Connection to the database failed. /// Check if the database is running and accessible. #[error("Connection failed")] ConnectionFailed, /// The query timed out. /// Consider optimizing the query or increasing timeout. #[error("Query timeout")] Timeout, }

27. 错误处理与性能分析

27.1 错误路径性能

分析错误路径的性能影响:

#[test] fn bench_error_path() { let mut group = criterion::BenchmarkGroup::new("errors"); group.bench_function("success", |b| { b.iter(|| Ok::<(), Error>(())) }); group.bench_function("error", |b| { b.iter(|| Err::<(), Error>(Error::Test)) }); group.finish(); }

27.2 错误处理开销优化

优化频繁调用的错误处理:

// 热点路径中使用 Option 代替 Result fn hot_path(input: &str) -> Option<u32> { if input.len() > 10 { return None; } Some(input.parse().ok()?) }

28. 错误处理与并发安全

28.1 线程安全错误类型

确保错误类型可以跨线程传递:

#[derive(Debug, thiserror::Error)] enum ThreadSafeError { #[error("IO Error")] Io(#[from] std::io::Error), #[error("Calculation Error")] Calc(String), } // 自动实现 Send + Sync unsafe impl Send for ThreadSafeError {} unsafe impl Sync for ThreadSafeError {}

28.2 原子错误报告

在多线程环境中原子地报告错误:

use std::sync::atomic::{AtomicBool, Ordering}; static HAS_ERROR: AtomicBool = AtomicBool::new(false); fn worker_thread() { if let Err(e) = do_work() { log_error(e); HAS_ERROR.store(true, Ordering::Relaxed); } } fn main() { let handles: Vec<_> = (0..4).map(|_| thread::spawn(worker_thread)).collect(); for handle in handles { handle.join().unwrap(); } if HAS_ERROR.load(Ordering::Relaxed) { eprintln!("One or more workers failed"); } }

29. 错误处理与资源清理

29.1 RAII 模式中的错误处理

结合 RAII 进行资源清理:

struct TempFile { path: PathBuf, } impl TempFile { fn new() -> Result<Self, io::Error> { let path = /* 创建临时文件 */; Ok(Self { path }) } fn write(&self, data: &[u8]) -> Result<(), io::Error> { // 写入文件 Ok(()) } } impl Drop for TempFile { fn drop(&mut self) { if let Err(e) = fs::remove_file(&self.path) { eprintln!("Failed to remove temp file: {}", e); } } }

29.2 事务性操作

实现事务性操作模式:

fn transactional_operation() -> Result<(), Error> { let resource1 = acquire_resource()?; let resource2 = acquire_another()?; let result = (|| { step1(&resource1)?; step2(&resource2)?; Ok(()) })(); if result.is_err() { cleanup(&resource1); cleanup(&resource2); } result }

30. 错误处理与用户界面

30.1 CLI 错误展示

为命令行应用设计友好的错误展示:

use colored::Colorize; fn main() { if let Err(e) = run_app() { eprintln!("{}: {}", "Error".red().bold(), e); if let Some(source) = e.source() { eprintln!("\n{}: {}", "Caused by".yellow(), source); } std::process::exit(1); } }

30.2 GUI 错误处理

在 GUI 应用中处理错误:

fn update_gui_state(&mut self) { match self.backend.load_data() { Ok(data) => self.display_data(data), Err(e) => { self.show_error_dialog(&format!("Failed to load data: {}", e)); self.set_loading(false); } } }

31. 错误处理与配置管理

31.1 配置验证

验证配置时提供有用的错误信息:

fn validate_config(config: &Config) -> Result<(), Vec<ConfigError>> { let mut errors = Vec::new(); if config.port == 0 { errors.push(ConfigError::InvalidPort); } if config.host.is_empty() { errors.push(ConfigError::MissingHost); } if errors.is_empty() { Ok(()) } else { Err(errors) } }

31.2 配置加载策略

实现灵活的配置加载策略:

fn load_config() -> Result<Config, Error> { let mut last_error = None; for path in possible_config_locations() { match Config::from_file(path) { Ok(cfg) => return Ok(cfg), Err(e) => last_error = Some(e), } } Err(last_error.unwrap_or(Error::NoConfigFound)) }

32. 错误处理与插件系统

32.1 插件加载错误

处理插件加载时的各种错误:

fn load_plugin(path: &Path) -> Result<Box<dyn Plugin>, PluginError> { unsafe { let lib = Library::new(path) .map_err(|e| PluginError::LoadFailed(e))?; let symbol: Symbol<fn() -> Box<dyn Plugin>> = lib.get(b"create_plugin") .map_err(|_| PluginError::InvalidSymbol)?; let plugin = symbol(); plugin.initialize() .map_err(|e| PluginError::InitFailed(e))?; Ok(plugin) } }

32.2 插件执行隔离

隔离插件执行防止崩溃影响主程序:

fn run_plugin_safely(plugin: &mut dyn Plugin, input: &str) -> Result<String, PluginError> { let result = panic::catch_unwind(|| plugin.process(input)); match result { Ok(Ok(output)) => Ok(output), Ok(Err(e)) => Err(PluginError::ExecutionFailed(e)), Err(_) => Err(PluginError::Panicked), } }

33. 错误处理与网络编程

33.1 网络错误分类

处理不同类型的网络错误:

fn handle_network_error(e: io::Error) -> Action { match e.kind() { io::ErrorKind::ConnectionRefused => Action::RetryAfterDelay, io::ErrorKind::TimedOut => Action::RetryImmediately, io::ErrorKind::PermissionDen