Rust 错误处理完全指南
📚 一、Rust 错误处理哲学
Rust 将错误分为两类:
- 可恢复错误(Recoverable):使用
Result<T, E>类型 - 不可恢复错误(Unrecoverable):使用
panic!宏
🎯 二、基础错误处理
1. panic! 宏 - 不可恢复错误
fn panic_example() {
// 1. 直接调用 panic!
// panic!("程序崩溃了!");
// 2. 断言失败时 panic
let x = 10;
assert!(x > 0, "x 必须大于 0");
// 3. 断言相等
assert_eq!(x, 10, "x 应该等于 10");
// 4. 不可达代码
if x > 100 {
unreachable!("x 不应该大于 100");
}
// 5. 调试断言(只在 debug 构建中检查)
debug_assert!(x > 0);
}
2. Result<T, E> 类型 - 可恢复错误
use std::fs::File;
use std::io::{Read, Error};
fn basic_result() -> Result<String, Error> {
// 打开文件可能失败
let mut file = File::open("hello.txt")?;
let mut content = String::new();
// 读取文件可能失败
file.read_to_string(&mut content)?;
Ok(content)
}
fn handle_result() {
match basic_result() {
Ok(content) => println!("文件内容: {}", content),
Err(e) => println!("读取文件失败: {}", e),
}
// 使用 if let
if let Ok(content) = basic_result() {
println!("内容: {}", content);
}
// 使用 unwrap 或 expect
let content = basic_result().unwrap(); // 失败时 panic
let content = basic_result().expect("读取文件失败"); // 失败时 panic 并显示信息
}
🔧 三、Result 的实用方法
1. 解包方法
fn unwrap_methods() {
let ok_result: Result<i32, &str> = Ok(42);
let err_result: Result<i32, &str> = Err("出错了");
// 1. unwrap - 成功返回值,失败 panic
let value1 = ok_result.unwrap(); // 42
// let value2 = err_result.unwrap(); // ❌ panic!
// 2. expect - 同 unwrap,但可自定义错误信息
let value3 = ok_result.expect("不应该出错"); // 42
// let value4 = err_result.expect("自定义错误信息"); // ❌ panic!
// 3. unwrap_or - 成功返回值,失败返回默认值
let value5 = ok_result.unwrap_or(0); // 42
let value6 = err_result.unwrap_or(0); // 0
// 4. unwrap_or_else - 成功返回值,失败执行闭包
let value7 = err_result.unwrap_or_else(|err| {
println!("错误: {}", err);
0
}); // 0
// 5. unwrap_or_default - 成功返回值,失败返回默认值
let ok_string: Result<String, &str> = Ok("hello".to_string());
let err_string: Result<String, &str> = Err("错误");
let s1 = ok_string.unwrap_or_default(); // "hello"
let s2 = err_string.unwrap_or_default(); // "" (空字符串)
}
2. 查询方法
fn query_methods() {
let ok_result: Result<i32, &str> = Ok(42);
let err_result: Result<i32, &str> = Err("出错了");
// 1. is_ok / is_err - 检查结果
println!("ok_result.is_ok(): {}", ok_result.is_ok()); // true
println!("ok_result.is_err(): {}", ok_result.is_err()); // false
// 2. as_ref / as_mut - 转换为引用
let ok_ref: Result<&i32, &str> = ok_result.as_ref();
let ok_mut: Result<&mut i32, &mut str> = ok_result.as_mut();
// 3. ok - 转换为 Option
let option1: Option<i32> = ok_result.ok(); // Some(42)
let option2: Option<i32> = err_result.ok(); // None
// 4. err - 获取错误
let error1: Option<&str> = ok_result.err(); // None
let error2: Option<&str> = err_result.err(); // Some("出错了")
}
3. 转换方法
fn transform_methods() {
let result: Result<i32, &str> = Ok(42);
// 1. map - 转换成功值
let doubled: Result<i32, &str> = result.map(|x| x * 2); // Ok(84)
// 2. map_err - 转换错误值
let new_error: Result<i32, String> = result.map_err(|e| e.to_string());
// 3. and_then - 链式调用
let chained: Result<String, &str> = result.and_then(|x| {
if x > 0 {
Ok(x.to_string())
} else {
Err("值太小")
}
});
// 4. or_else - 处理错误
let recovered: Result<i32, &str> = Err("错误").or_else(|_| Ok(100));
// 5. and / or - 组合 Result
let ok1: Result<i32, &str> = Ok(1);
let ok2: Result<i32, &str> = Ok(2);
let err: Result<i32, &str> = Err("错误");
let and_result = ok1.and(ok2); // Ok(2)
let or_result = err.or(ok1); // Ok(1)
}
🎨 四、自定义错误类型
1. 使用枚举定义错误
use std::fmt;
use std::io;
#[derive(Debug)]
enum AppError {
Io(io::Error), // IO 错误
Parse(std::num::ParseIntError), // 解析错误
Custom(String), // 自定义错误
Overflow, // 溢出错误
NotFound, // 未找到错误
}
// 实现 Display trait
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
AppError::Io(e) => write!(f, "IO错误: {}", e),
AppError::Parse(e) => write!(f, "解析错误: {}", e),
AppError::Custom(msg) => write!(f, "自定义错误: {}", msg),
AppError::Overflow => write!(f, "数值溢出错误"),
AppError::NotFound => write!(f, "未找到资源"),
}
}
}
// 实现 Error trait
impl std::error::Error for AppError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
AppError::Io(e) => Some(e),
AppError::Parse(e) => Some(e),
_ => None,
}
}
}
// 实现 From trait 用于自动转换
impl From<io::Error> for AppError {
fn from(e: io::Error) -> Self {
AppError::Io(e)
}
}
impl From<std::num::ParseIntError> for AppError {
fn from(e: std::num::ParseIntError) -> Self {
AppError::Parse(e)
}
}
// 使用示例
fn process_file(filename: &str) -> Result<i32, AppError> {
use std::fs;
let content = fs::read_to_string(filename)?; // 自动转换为 AppError::Io
let number: i32 = content.trim().parse()?; // 自动转换为 AppError::Parse
if number > 1000 {
return Err(AppError::Overflow);
}
Ok(number)
}
2. 使用 thiserror 库简化
[dependencies]
thiserror = "1.0"
use thiserror::Error;
use std::io;
#[derive(Error, Debug)]
enum AppError {
#[error("IO错误: {0}")]
Io(#[from] io::Error),
#[error("解析错误: {0}")]
Parse(#[from] std::num::ParseIntError),
#[error("自定义错误: {0}")]
Custom(String),
#[error("数值溢出错误")]
Overflow,
#[error("未找到资源: {0}")]
NotFound(String),
#[error("配置错误: {0}")]
Config(#[from] ConfigError),
}
#[derive(Error, Debug)]
enum ConfigError {
#[error("缺少配置项: {0}")]
Missing(String),
#[error("无效配置: {0}")]
Invalid(String),
}
fn load_config() -> Result<(), ConfigError> {
Err(ConfigError::Missing("api_key".to_string()))
}
fn process() -> Result<(), AppError> {
load_config()?; // 自动转换为 AppError::Config
Ok(())
}
3. 使用 anyhow 库简化应用程序错误
[dependencies]
anyhow = "1.0"
use anyhow::{Context, Result, bail, ensure};
use std::fs;
fn process_file(path: &str) -> Result<String> {
// 使用 context 添加错误上下文
let content = fs::read_to_string(path)
.context(format!("读取文件失败: {}", path))?;
// 使用 ensure 进行条件检查
ensure!(!content.is_empty(), "文件内容为空");
// 使用 bail 提前返回错误
if content.len() > 1000 {
bail!("文件太大: {} 字节", content.len());
}
Ok(content)
}
fn complex_operation() -> Result<()> {
let data1 = process_file("file1.txt")?;
let data2 = process_file("file2.txt")?;
// 使用 with_context 链式添加上下文
let result = process_file("file3.txt")
.with_context(|| format!("处理组合数据失败: {}, {}", data1, data2))?;
Ok(())
}
🔄 五、错误传播模式
1. ? 运算符
use std::fs;
use std::io;
// 传统写法
fn read_file_old(path: &str) -> Result<String, io::Error> {
let file = match fs::File::open(path) {
Ok(file) => file,
Err(e) => return Err(e),
};
// ... 更多处理
Ok(String::new())
}
// 使用 ? 运算符
fn read_file_new(path: &str) -> Result<String, io::Error> {
let content = fs::read_to_string(path)?;
Ok(content)
}
// 链式调用
fn process_data() -> Result<(), Box<dyn std::error::Error>> {
let data1 = fs::read_to_string("file1.txt")?;
let data2 = fs::read_to_string("file2.txt")?;
let data3 = fs::read_to_string("file3.txt")?;
// 处理数据...
Ok(())
}
2. 错误链和上下文
use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
fn load_config_file() -> Result<String> {
let paths = [
"./config.toml",
"~/.config/app/config.toml",
"/etc/app/config.toml",
];
for path in &paths {
if Path::new(path).exists() {
return fs::read_to_string(path)
.with_context(|| format!("无法读取配置文件: {}", path));
}
}
anyhow::bail!("未找到配置文件");
}
fn parse_config() -> Result<Config> {
let content = load_config_file()?;
// 解析配置
let config: Config = toml::from_str(&content)
.context("解析配置文件失败")?;
Ok(config)
}
struct Config;
🏗️ 六、错误处理模式
1. 错误包装模式
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct OperationError {
operation: String,
source: Box<dyn Error>,
}
impl fmt::Display for OperationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "操作 '{}' 失败: {}", self.operation, self.source)
}
}
impl Error for OperationError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(self.source.as_ref())
}
}
// 包装器函数
fn wrap_error<T, E>(result: Result<T, E>, operation: &str) -> Result<T, OperationError>
where
E: Error + 'static,
{
result.map_err(|e| OperationError {
operation: operation.to_string(),
source: Box::new(e),
})
}
// 使用示例
fn process() -> Result<(), Box<dyn Error>> {
let data = wrap_error(std::fs::read_to_string("data.txt"), "读取文件")?;
let parsed = wrap_error(data.parse::<i32>(), "解析数字")?;
Ok(())
}
2. 错误类型转换
use std::num::ParseIntError;
fn parse_and_multiply(a: &str, b: &str) -> Result<i32, String> {
let x = a.parse::<i32>()
.map_err(|e: ParseIntError| format!("解析 {} 失败: {}", a, e))?;
let y = b.parse::<i32>()
.map_err(|e| format!("解析 {} 失败: {}", b, e))?;
x.checked_mul(y)
.ok_or_else(|| "乘法溢出".to_string())
}
// 使用 map_err 和 or_else
fn process_data(data: &str) -> Result<i32, String> {
data.parse::<i32>()
.map_err(|e| e.to_string())
.and_then(|n| {
if n > 0 {
Ok(n)
} else {
Err("数字必须为正数".to_string())
}
})
}
📁 七、文件和 I/O 错误处理
use std::fs;
use std::io;
use std::path::Path;
#[derive(Debug)]
enum FileError {
NotFound(String),
PermissionDenied(String),
Io(io::Error),
InvalidFormat(String),
}
impl std::error::Error for FileError {}
impl std::fmt::Display for FileError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FileError::NotFound(path) => write!(f, "文件未找到: {}", path),
FileError::PermissionDenied(path) => write!(f, "权限不足: {}", path),
FileError::Io(e) => write!(f, "IO错误: {}", e),
FileError::InvalidFormat(msg) => write!(f, "格式错误: {}", msg),
}
}
}
impl From<io::Error> for FileError {
fn from(e: io::Error) -> Self {
match e.kind() {
io::ErrorKind::NotFound => FileError::NotFound(e.to_string()),
io::ErrorKind::PermissionDenied => FileError::PermissionDenied(e.to_string()),
_ => FileError::Io(e),
}
}
}
fn process_file_safely(path: &str) -> Result<String, FileError> {
// 检查文件是否存在
if !Path::new(path).exists() {
return Err(FileError::NotFound(path.to_string()));
}
// 读取文件
let content = fs::read_to_string(path)?;
// 验证内容
if content.trim().is_empty() {
return Err(FileError::InvalidFormat("文件为空".to_string()));
}
Ok(content)
}
// 批量处理文件
fn process_multiple_files(paths: &[&str]) -> Vec<Result<String, FileError>> {
paths.iter()
.map(|&path| process_file_safely(path))
.collect()
}
🔧 八、网络和 HTTP 错误处理
use reqwest;
use serde_json;
use thiserror::Error;
use std::time::Duration;
#[derive(Error, Debug)]
enum ApiError {
#[error("网络请求失败: {0}")]
Network(#[from] reqwest::Error),
#[error("解析JSON失败: {0}")]
Parse(#[from] serde_json::Error),
#[error("API错误: {0} - {1}")]
Http(u16, String),
#[error("超时")]
Timeout,
#[error("认证失败")]
Unauthorized,
#[error("资源未找到")]
NotFound,
#[error("服务器错误: {0}")]
Server(String),
}
struct ApiClient {
client: reqwest::Client,
base_url: String,
}
impl ApiClient {
fn new(base_url: String) -> Self {
let client = reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.build()
.unwrap();
ApiClient { client, base_url }
}
async fn get_user(&self, user_id: i32) -> Result<User, ApiError> {
let url = format!("{}/users/{}", self.base_url, user_id);
let response = self.client
.get(&url)
.send()
.await
.map_err(ApiError::Network)?;
// 处理 HTTP 状态码
match response.status().as_u16() {
200 => {
let user: User = response.json().await?;
Ok(user)
}
401 => Err(ApiError::Unauthorized),
404 => Err(ApiError::NotFound),
500..=599 => Err(ApiError::Server(response.status().to_string())),
status => {
let text = response.text().await.unwrap_or_default();
Err(ApiError::Http(status, text))
}
}
}
// 重试逻辑
async fn get_user_with_retry(&self, user_id: i32, max_retries: u32) -> Result<User, ApiError> {
let mut retries = 0;
loop {
match self.get_user(user_id).await {
Ok(user) => return Ok(user),
Err(ApiError::Network(_) | ApiError::Timeout) if retries < max_retries => {
retries += 1;
tokio::time::sleep(Duration::from_secs(1 << retries)).await; // 指数退避
}
Err(e) => return Err(e),
}
}
}
}
struct User;
🧪 九、测试中的错误处理
#[cfg(test)]
mod tests {
use super::*;
// 测试正常情况
#[test]
fn test_success() -> Result<(), String> {
let result = parse_and_multiply("2", "3")?;
assert_eq!(result, 6);
Ok(())
}
// 测试错误情况
#[test]
fn test_parse_error() {
let result = parse_and_multiply("abc", "3");
assert!(result.is_err());
if let Err(msg) = result {
assert!(msg.contains("解析 abc 失败"));
}
}
// 测试 panic
#[test]
#[should_panic(expected = "除数不能为零")]
fn test_divide_by_zero() {
divide(10, 0);
}
#[test]
#[should_panic]
fn test_unwrap_panic() {
let result: Result<i32, &str> = Err("错误");
result.unwrap();
}
// 使用 assert
#[test]
fn test_with_assert() {
let result = parse_and_multiply("2", "3");
assert!(result.is_ok());
let value = result.unwrap();
assert_eq!(value, 6);
assert!(parse_and_multiply("a", "3").is_err());
}
// 测试特定错误类型
#[test]
fn test_error_types() {
use std::io;
let io_error = io::Error::new(io::ErrorKind::NotFound, "文件未找到");
let app_error = AppError::Io(io_error);
match app_error {
AppError::Io(e) => {
assert_eq!(e.kind(), io::ErrorKind::NotFound);
}
_ => panic!("应该是 Io 错误"),
}
}
}
fn divide(a: i32, b: i32) -> i32 {
if b == 0 {
panic!("除数不能为零");
}
a / b
}
⚡ 十、性能考虑
1. 避免不必要的错误分配
use std::error::Error;
use std::fmt;
// 使用静态错误消息避免分配
#[derive(Debug)]
enum StaticError {
InvalidInput,
OutOfBounds,
NetworkError,
}
impl Error for StaticError {}
impl fmt::Display for StaticError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
StaticError::InvalidInput => write!(f, "无效输入"),
StaticError::OutOfBounds => write!(f, "超出范围"),
StaticError::NetworkError => write!(f, "网络错误"),
}
}
}
// 使用 Cow 避免不必要的克隆
use std::borrow::Cow;
#[derive(Debug)]
enum SmartError<'a> {
Static(&'static str),
Dynamic(Cow<'a, str>),
}
impl<'a> SmartError<'a> {
fn new_static(msg: &'static str) -> Self {
SmartError::Static(msg)
}
fn new_dynamic<S: Into<Cow<'a, str>>>(msg: S) -> Self {
SmartError::Dynamic(msg.into())
}
}
2. 错误缓存
use std::sync::OnceLock;
use std::error::Error;
use std::fmt;
#[derive(Debug)]
struct CachedError {
message: &'static str,
}
impl Error for CachedError {}
impl fmt::Display for CachedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
// 使用 OnceLock 缓存常见错误
static COMMON_ERRORS: OnceLock<CommonErrors> = OnceLock::new();
struct CommonErrors {
not_found: CachedError,
timeout: CachedError,
invalid_input: CachedError,
}
impl CommonErrors {
fn global() -> &'static Self {
COMMON_ERRORS.get_or_init(|| CommonErrors {
not_found: CachedError { message: "未找到" },
timeout: CachedError { message: "超时" },
invalid_input: CachedError { message: "无效输入" },
})
}
}
📋 十一、错误处理最佳实践
1. 库 vs 应用程序
// 库代码:提供具体的错误类型
pub mod my_lib {
use thiserror::Error;
#[derive(Error, Debug)]
pub enum LibError {
#[error("配置错误: {0}")]
Config(String),
#[error("IO错误: {0}")]
Io(#[from] std::io::Error),
}
pub fn library_function() -> Result<(), LibError> {
// 库函数返回具体的错误类型
Ok(())
}
}
// 应用程序:使用 anyhow 简化错误处理
use anyhow::Result;
mod my_lib;
fn application_function() -> Result<()> {
// 应用程序使用 anyhow::Result
my_lib::library_function()?;
Ok(())
}
2. 错误处理模式选择
fn error_handling_patterns() {
// 模式1: 使用 match 明确处理
let result: Result<i32, &str> = Ok(42);
match result {
Ok(value) => println!("成功: {}", value),
Err(e) => println!("错误: {}", e),
}
// 模式2: 使用 if let
if let Ok(value) = result {
println!("值: {}", value);
}
// 模式3: 使用 map_or
let value = result.map_or(0, |v| v);
// 模式4: 使用 unwrap_or_else
let value = result.unwrap_or_else(|err| {
println!("使用默认值,错误: {}", err);
0
});
// 模式5: 使用 ? 传播错误
fn propagate() -> Result<(), &str> {
let _ = result?;
Ok(())
}
}
3. 日志和监控
use log::{error, warn, info};
use anyhow::Result;
fn process_with_logging() -> Result<()> {
// 记录不同级别的日志
info!("开始处理");
match risky_operation() {
Ok(result) => {
info!("操作成功: {:?}", result);
Ok(())
}
Err(e) => {
// 记录错误但不崩溃
error!("操作失败: {}", e);
// 记录完整的错误链
let mut source = e.source();
while let Some(err) = source {
warn!("原因: {}", err);
source = err.source();
}
// 返回错误
Err(e)
}
}
}
fn risky_operation() -> Result<String> {
// 模拟可能失败的操作
if rand::random() {
Ok("成功".to_string())
} else {
anyhow::bail!("随机失败")
}
}
📦 十二、完整示例:配置文件加载器
use anyhow::{Context, Result, bail};
use serde::Deserialize;
use std::fs;
use std::path::{Path, PathBuf};
use log::{info, warn, error};
use thiserror::Error;
#[derive(Error, Debug)]
enum ConfigError {
#[error("配置文件未找到")]
NotFound,
#[error("配置文件格式错误: {0}")]
InvalidFormat(String),
#[error("配置文件验证失败: {0}")]
Validation(String),
}
#[derive(Debug, Deserialize)]
struct Config {
server: ServerConfig,
database: DatabaseConfig,
logging: LoggingConfig,
}
#[derive(Debug, Deserialize)]
struct ServerConfig {
host: String,
port: u16,
timeout: u64,
}
#[derive(Debug, Deserialize)]
struct DatabaseConfig {
url: String,
pool_size: u32,
timeout: u64,
}
#[derive(Debug, Deserialize)]
struct LoggingConfig {
level: String,
file: Option<PathBuf>,
}
impl Config {
fn validate(&self) -> Result<(), ConfigError> {
if self.server.port == 0 {
return Err(ConfigError::Validation("端口不能为0".to_string()));
}
if self.database.pool_size == 0 {
return Err(ConfigError::Validation("数据库连接池大小不能为0".to_string()));
}
Ok(())
}
}
struct ConfigLoader {
search_paths: Vec<PathBuf>,
}
impl ConfigLoader {
fn new() -> Self {
let mut search_paths = vec![
PathBuf::from("./config.toml"),
PathBuf::from("~/.config/app/config.toml"),
PathBuf::from("/etc/app/config.toml"),
];
// 添加环境变量指定的路径
if let Ok(env_path) = std::env::var("APP_CONFIG_PATH") {
search_paths.insert(0, PathBuf::from(env_path));
}
ConfigLoader { search_paths }
}
fn find_config_file(&self) -> Result<PathBuf, ConfigError> {
for path in &self.search_paths {
if path.exists() {
info!("找到配置文件: {:?}", path);
return Ok(path.clone());
}
}
warn!("未找到配置文件,搜索路径: {:?}", self.search_paths);
Err(ConfigError::NotFound)
}
fn load(&self) -> Result<Config, anyhow::Error> {
// 查找配置文件
let config_path = self.find_config_file()
.context("查找配置文件失败")?;
// 读取文件
let content = fs::read_to_string(&config_path)
.with_context(|| format!("读取配置文件失败: {:?}", config_path))?;
// 解析配置
let config: Config = toml::from_str(&content)
.map_err(|e| {
ConfigError::InvalidFormat(e.to_string())
})
.with_context(|| format!("解析配置文件失败: {:?}", config_path))?;
// 验证配置
config.validate()
.context("配置验证失败")?;
info!("配置加载成功");
Ok(config)
}
fn load_with_fallback(&self) -> Result<Config, anyhow::Error> {
match self.load() {
Ok(config) => Ok(config),
Err(e) => {
error!("加载配置失败: {}", e);
// 尝试使用默认配置
warn!("使用默认配置");
self.create_default_config()
}
}
}
fn create_default_config(&self) -> Result<Config, anyhow::Error> {
let default_config = Config {
server: ServerConfig {
host: "localhost".to_string(),
port: 8080,
timeout: 30,
},
database: DatabaseConfig {
url: "postgresql://localhost:5432/app".to_string(),
pool_size: 10,
timeout: 5,
},
logging: LoggingConfig {
level: "info".to_string(),
file: None,
},
};
// 保存默认配置
let default_path = Path::new("./config.default.toml");
let toml = toml::to_string_pretty(&default_config)?;
fs::write(default_path, toml)
.context("保存默认配置失败")?;
info!("已创建默认配置文件: {:?}", default_path);
Ok(default_config)
}
}
// 主程序
fn main() -> Result<()> {
// 初始化日志
env_logger::init();
// 创建配置加载器
let loader = ConfigLoader::new();
// 加载配置
let config = loader.load_with_fallback()?;
// 使用配置
println!("服务器配置: {}:{}", config.server.host, config.server.port);
println!("数据库配置: {}", config.database.url);
println!("日志级别: {}", config.logging.level);
Ok(())
}
🎯 十三、总结要点
- 区分错误类型:可恢复错误用
Result,不可恢复错误用panic - 自定义错误类型:使用枚举定义清晰的错误类型
- 使用适当的库:库代码用
thiserror,应用程序用anyhow - 提供错误上下文:使用
context和with_context添加信息 - 错误传播:合理使用
?运算符 - 错误处理策略:根据场景选择匹配、解包或传播
- 日志记录:记录错误以便调试和监控
- 性能考虑:避免不必要的错误分配
- 测试错误:确保错误情况被正确处理
Rust 的错误处理系统是其最强大的特性之一,通过编译时检查确保错误的正确处理,避免了未处理异常导致的问题。正确使用错误处理可以编写出既安全又健壮的程序。