本章概述
错误处理是编程中的重要主题,Rust通过其类型系统提供了安全且表达力强的错误处理机制。与许多语言使用异常不同,Rust使用Result和Option类型来显式处理错误和空值情况。本章将深入探讨Rust的错误处理哲学和实践。
学习目标
- 🎯 理解Rust错误处理的设计哲学
- 🎯 掌握
Result和Option类型的使用 - 🎯 学会错误传播和处理策略
- 🎯 了解如何创建自定义错误类型
- 🎯 掌握错误处理的最佳实践
- 🎯 学会使用
?操作符简化错误处理
8.1 错误处理基础
8.1.1 Rust的错误处理哲学
fn main() {
println!("🚨 Rust错误处理哲学");
println!("==================");
demonstrate_explicit_errors();
demonstrate_recoverable_vs_unrecoverable();
demonstrate_type_safety();
}
// 显式错误处理
fn demonstrate_explicit_errors() {
println!("\n1. 显式错误处理:");
println!(" - 错误是类型系统的一部分");
println!(" - 必须显式处理或传播错误");
println!(" - 编译器强制错误处理");
// 示例:文件读取可能失败
match std::fs::read_to_string("nonexistent.txt") {
Ok(content) => println!("文件内容: {}", content),
Err(error) => println!("读取文件失败: {}", error),
}
// 示例:字符串解析可能失败
let number_str = "42";
match number_str.parse::<i32>() {
Ok(number) => println!("解析成功: {}", number),
Err(error) => println!("解析失败: {}", error),
}
}
// 可恢复 vs 不可恢复错误
fn demonstrate_recoverable_vs_unrecoverable() {
println!("\n2. 错误类型分类:");
println!(" - 可恢复错误: 使用Result<T, E>");
println!(" - 不可恢复错误: 使用panic!");
// 可恢复错误示例
fn divide(a: f64, b: f64) -> Result<f64, String> {
if b == 0.0 {
Err("除零错误".to_string())
} else {
Ok(a / b)
}
}
match divide(10.0, 2.0) {
Ok(result) => println!("除法结果: {}", result),
Err(error) => println!("除法错误: {}", error),
}
match divide(10.0, 0.0) {
Ok(result) => println!("除法结果: {}", result),
Err(error) => println!("除法错误: {}", error),
}
// 不可恢复错误示例(注释掉以避免程序崩溃)
// panic!("这是一个不可恢复的错误!");
println!(" 注意: panic!会导致程序崩溃,只在无法恢复的情况下使用");
}
// 类型安全
fn demonstrate_type_safety() {
println!("\n3. 类型安全:");
println!(" - 错误类型在编译时确定");
println!(" - 防止忘记处理错误");
println!(" - 提供丰富的错误信息");
// 类型安全的错误处理
#[derive(Debug)]
enum MathError {
DivisionByZero,
NegativeSquareRoot,
Overflow,
}
fn safe_divide(a: i32, b: i32) -> Result<i32, MathError> {
if b == 0 {
Err(MathError::DivisionByZero)
} else {
match a.checked_div(b) {
Some(result) => Ok(result),
None => Err(MathError::Overflow),
}
}
}
fn safe_sqrt(x: f64) -> Result<f64, MathError> {
if x < 0.0 {
Err(MathError::NegativeSquareRoot)
} else {
Ok(x.sqrt())
}
}
// 使用类型安全的错误处理
match safe_divide(10, 2) {
Ok(result) => println!("安全除法: 10 / 2 = {}", result),
Err(error) => println!("除法错误: {:?}", error),
}
match safe_sqrt(-4.0) {
Ok(result) => println!("平方根: {}", result),
Err(error) => println!("平方根错误: {:?}", error),
}
}
rustc error_philosophy.rs && ./error_philosophy
8.1.2 Option类型详解
fn main() {
println!("🔍 Option类型深入");
println!("================");
demonstrate_option_basics();
demonstrate_option_methods();
demonstrate_option_patterns();
demonstrate_option_chaining();
}
fn demonstrate_option_basics() {
println!("\nOption基础:");
// Option表示可能存在或不存在的值
let some_number = Some(42);
let no_number: Option<i32> = None;
println!("some_number: {:?}", some_number);
println!("no_number: {:?}", no_number);
// 使用match处理Option
match some_number {
Some(value) => println!("找到值: {}", value),
None => println!("没有值"),
}
match no_number {
Some(value) => println!("找到值: {}", value),
None => println!("没有值"),
}
// 实际应用:查找数组中的元素
let numbers = vec![1, 2, 3, 4, 5];
fn find_even(numbers: &[i32]) -> Option<i32> {
for &num in numbers {
if num % 2 == 0 {
return Some(num);
}
}
None
}
match find_even(&numbers) {
Some(even) => println!("找到第一个偶数: {}", even),
None => println!("没有找到偶数"),
}
}
fn demonstrate_option_methods() {
println!("\nOption常用方法:");
let some_value = Some("hello");
let no_value: Option<&str> = None;
// is_some() 和 is_none()
println!("some_value.is_some(): {}", some_value.is_some());
println!("some_value.is_none(): {}", some_value.is_none());
println!("no_value.is_some(): {}", no_value.is_some());
println!("no_value.is_none(): {}", no_value.is_none());
// unwrap() - 获取值,如果是None则panic
println!("some_value.unwrap(): {}", some_value.unwrap());
// no_value.unwrap(); // 这会panic!
// unwrap_or() - 提供默认值
println!("some_value.unwrap_or(\"default\"): {}", some_value.unwrap_or("default"));
println!("no_value.unwrap_or(\"default\"): {}", no_value.unwrap_or("default"));
// unwrap_or_else() - 使用闭包计算默认值
println!("no_value.unwrap_or_else(): {}",
no_value.unwrap_or_else(|| "computed default"));
// expect() - 带自定义错误消息的unwrap
println!("some_value.expect(): {}", some_value.expect("应该有值"));
// map() - 转换Option中的值
let number = Some(42);
let doubled = number.map(|x| x * 2);
println!("number.map(|x| x * 2): {:?}", doubled);
let no_number: Option<i32> = None;
let doubled_none = no_number.map(|x| x * 2);
println!("no_number.map(|x| x * 2): {:?}", doubled_none);
// and_then() - 链式操作
let result = Some("42")
.and_then(|s| s.parse::<i32>().ok())
.and_then(|n| if n > 0 { Some(n * 2) } else { None });
println!("链式操作结果: {:?}", result);
// filter() - 过滤值
let filtered = Some(42).filter(|&x| x > 40);
println!("Some(42).filter(|&x| x > 40): {:?}", filtered);
let filtered_out = Some(30).filter(|&x| x > 40);
println!("Some(30).filter(|&x| x > 40): {:?}", filtered_out);
}
fn demonstrate_option_patterns() {
println!("\nOption模式匹配:");
let values = vec![Some(1), None, Some(3), Some(4), None];
// 基本模式匹配
for (i, value) in values.iter().enumerate() {
match value {
Some(n) => println!("位置 {}: 值 = {}", i, n),
None => println!("位置 {}: 无值", i),
}
}
// if let 模式
println!("\n使用 if let:");
for (i, value) in values.iter().enumerate() {
if let Some(n) = value {
println!("位置 {}: 值 = {}", i, n);
}
}
// while let 模式
println!("\n使用 while let:");
let mut iter = values.iter();
while let Some(option_value) = iter.next() {
if let Some(value) = option_value {
println!("迭代到值: {}", value);
} else {
println!("迭代到None");
}
}
// 嵌套Option
let nested: Option<Option<i32>> = Some(Some(42));
match nested {
Some(Some(value)) => println!("嵌套值: {}", value),
Some(None) => println!("外层Some,内层None"),
None => println!("外层None"),
}
// flatten() 展平嵌套Option
let flattened = nested.flatten();
println!("展平后: {:?}", flattened);
}
fn demonstrate_option_chaining() {
println!("\nOption链式操作:");
#[derive(Debug)]
struct Person {
name: String,
age: Option<u32>,
address: Option<Address>,
}
#[derive(Debug)]
struct Address {
street: String,
city: String,
postal_code: Option<String>,
}
let person = Person {
name: "张三".to_string(),
age: Some(30),
address: Some(Address {
street: "中山路123号".to_string(),
city: "北京".to_string(),
postal_code: Some("100000".to_string()),
}),
};
// 安全地访问嵌套的Option字段
let postal_code = person.address
.as_ref()
.and_then(|addr| addr.postal_code.as_ref());
match postal_code {
Some(code) => println!("邮政编码: {}", code),
None => println!("没有邮政编码"),
}
// 使用map进行转换
let age_in_months = person.age.map(|age| age * 12);
println!("年龄(月): {:?}", age_in_months);
// 组合多个Option
fn format_person_info(person: &Person) -> Option<String> {
person.age.map(|age| {
format!("{} ({} 岁)", person.name, age)
})
}
if let Some(info) = format_person_info(&person) {
println!("人员信息: {}", info);
}
// 使用zip组合两个Option
let option1 = Some(10);
let option2 = Some(20);
let combined = option1.zip(option2).map(|(a, b)| a + b);
println!("组合结果: {:?}", combined);
// 收集Option向量
let options = vec![Some(1), Some(2), None, Some(4)];
let collected: Option<Vec<i32>> = options.into_iter().collect();
println!("收集结果: {:?}", collected); // None,因为包含None
let options2 = vec![Some(1), Some(2), Some(3), Some(4)];
let collected2: Option<Vec<i32>> = options2.into_iter().collect();
println!("收集结果2: {:?}", collected2); // Some([1, 2, 3, 4])
}
rustc option_deep_dive.rs && ./option_deep_dive
8.2 Result类型详解
8.2.1 Result基础和方法
fn main() {
println!("📊 Result类型深入");
println!("================");
demonstrate_result_basics();
demonstrate_result_methods();
demonstrate_result_patterns();
demonstrate_result_conversion();
}
fn demonstrate_result_basics() {
println!("\nResult基础:");
// Result表示可能成功或失败的操作
let success: Result<i32, String> = Ok(42);
let failure: Result<i32, String> = Err("出错了".to_string());
println!("success: {:?}", success);
println!("failure: {:?}", failure);
// 使用match处理Result
match success {
Ok(value) => println!("成功: {}", value),
Err(error) => println!("错误: {}", error),
}
match failure {
Ok(value) => println!("成功: {}", value),
Err(error) => println!("错误: {}", error),
}
// 实际应用:文件操作
fn read_file_size(filename: &str) -> Result<u64, std::io::Error> {
let metadata = std::fs::metadata(filename)?;
Ok(metadata.len())
}
match read_file_size("Cargo.toml") {
Ok(size) => println!("文件大小: {} 字节", size),
Err(error) => println!("读取文件失败: {}", error),
}
}
fn demonstrate_result_methods() {
println!("\nResult常用方法:");
let success: Result<i32, String> = Ok(42);
let failure: Result<i32, String> = Err("错误".to_string());
// is_ok() 和 is_err()
println!("success.is_ok(): {}", success.is_ok());
println!("success.is_err(): {}", success.is_err());
println!("failure.is_ok(): {}", failure.is_ok());
println!("failure.is_err(): {}", failure.is_err());
// unwrap() - 获取Ok值,如果是Err则panic
println!("success.unwrap(): {}", success.unwrap());
// failure.unwrap(); // 这会panic!
// unwrap_or() - 提供默认值
println!("success.unwrap_or(0): {}", success.unwrap_or(0));
println!("failure.unwrap_or(0): {}", failure.unwrap_or(0));
// unwrap_or_else() - 使用闭包处理错误
let result = failure.unwrap_or_else(|err| {
println!("处理错误: {}", err);
-1
});
println!("unwrap_or_else结果: {}", result);
// expect() - 带自定义错误消息的unwrap
println!("success.expect(): {}", success.expect("应该成功"));
// map() - 转换Ok值
let doubled = success.map(|x| x * 2);
println!("success.map(|x| x * 2): {:?}", doubled);
let doubled_err = failure.map(|x| x * 2);
println!("failure.map(|x| x * 2): {:?}", doubled_err);
// map_err() - 转换Err值
let mapped_err = failure.map_err(|err| format!("映射错误: {}", err));
println!("failure.map_err(): {:?}", mapped_err);
// and_then() - 链式操作(成功时)
let result = Ok("42")
.and_then(|s| s.parse::<i32>().map_err(|e| e.to_string()))
.and_then(|n| if n > 0 { Ok(n * 2) } else { Err("负数".to_string()) });
println!("链式操作结果: {:?}", result);
// or_else() - 链式操作(失败时)
let recovery = Err("第一次失败")
.or_else(|_| Ok("恢复成功"))
.or_else(|_| Err("最终失败"));
println!("错误恢复: {:?}", recovery);
}
fn demonstrate_result_patterns() {
println!("\nResult模式匹配:");
let results = vec![
Ok(1),
Err("错误1".to_string()),
Ok(3),
Err("错误2".to_string()),
];
// 基本模式匹配
for (i, result) in results.iter().enumerate() {
match result {
Ok(value) => println!("结果 {}: 成功 = {}", i, value),
Err(error) => println!("结果 {}: 错误 = {}", i, error),
}
}
// if let 模式
println!("\n使用 if let:");
for (i, result) in results.iter().enumerate() {
if let Ok(value) = result {
println!("结果 {}: 成功 = {}", i, value);
}
}
// 嵌套Result处理
fn parse_and_double(s: &str) -> Result<Result<i32, String>, std::num::ParseIntError> {
let parsed = s.parse::<i32>()?;
if parsed > 0 {
Ok(Ok(parsed * 2))
} else {
Ok(Err("负数或零".to_string()))
}
}
match parse_and_double("42") {
Ok(Ok(value)) => println!("嵌套成功: {}", value),
Ok(Err(error)) => println!("内层错误: {}", error),
Err(error) => println!("外层错误: {}", error),
}
// flatten() 展平嵌套Result
let nested: Result<Result<i32, String>, String> = Ok(Ok(42));
let flattened = nested.flatten();
println!("展平后: {:?}", flattened);
}
fn demonstrate_result_conversion() {
println!("\nResult转换:");
// Result和Option之间的转换
let result: Result<i32, String> = Ok(42);
let option = result.ok(); // Result -> Option
println!("Result转Option: {:?}", option);
let error_result: Result<i32, String> = Err("错误".to_string());
let none_option = error_result.ok();
println!("错误Result转Option: {:?}", none_option);
let option: Option<i32> = Some(42);
let result = option.ok_or("没有值".to_string()); // Option -> Result
println!("Option转Result: {:?}", result);
let none: Option<i32> = None;
let error_result = none.ok_or("没有值".to_string());
println!("None转Result: {:?}", error_result);
// 使用ok_or_else()提供动态错误
let dynamic_error = none.ok_or_else(|| {
format!("动态错误消息: {}", "没有值")
});
println!("动态错误: {:?}", dynamic_error);
// 收集Result向量
let results = vec![Ok(1), Ok(2), Ok(3)];
let collected: Result<Vec<i32>, String> = results.into_iter().collect();
println!("收集成功结果: {:?}", collected);
let mixed_results = vec![Ok(1), Err("错误".to_string()), Ok(3)];
let collected_mixed: Result<Vec<i32>, String> = mixed_results.into_iter().collect();
println!("收集混合结果: {:?}", collected_mixed); // 遇到第一个错误就停止
// 分离成功和失败
let results = vec![Ok(1), Err("错误1"), Ok(3), Err("错误2")];
let (successes, errors): (Vec<_>, Vec<_>) = results.into_iter().partition(Result::is_ok);
let success_values: Vec<i32> = successes.into_iter().map(Result::unwrap).collect();
let error_values: Vec<String> = errors.into_iter().map(Result::unwrap_err).collect();
println!("成功值: {:?}", success_values);
println!("错误值: {:?}", error_values);
}
rustc result_deep_dive.rs && ./result_deep_dive
8.2.2 错误传播和?操作符
use std::fs::File;
use std::io::{self, Read};
use std::num::ParseIntError;
fn main() {
println!("🔄 错误传播");
println!("==========");
demonstrate_manual_propagation();
demonstrate_question_mark_operator();
demonstrate_early_return();
demonstrate_multiple_error_types();
}
fn demonstrate_manual_propagation() {
println!("\n手动错误传播:");
// 传统的错误传播方式
fn read_file_manual(filename: &str) -> Result<String, io::Error> {
let mut file = match File::open(filename) {
Ok(file) => file,
Err(error) => return Err(error),
};
let mut contents = String::new();
match file.read_to_string(&mut contents) {
Ok(_) => Ok(contents),
Err(error) => Err(error),
}
}
match read_file_manual("Cargo.toml") {
Ok(contents) => println!("文件内容长度: {} 字符", contents.len()),
Err(error) => println!("读取失败: {}", error),
}
}
fn demonstrate_question_mark_operator() {
println!("\n?操作符:");
// 使用?操作符简化错误传播
fn read_file_simple(filename: &str) -> Result<String, io::Error> {
let mut file = File::open(filename)?;
let mut contents = String::new();
file.read_to_string(&mut contents)?;
Ok(contents)
}
// 更简洁的版本
fn read_file_concise(filename: &str) -> Result<String, io::Error> {
std::fs::read_to_string(filename)
}
match read_file_simple("Cargo.toml") {
Ok(contents) => println!("简化版文件内容长度: {} 字符", contents.len()),
Err(error) => println!("读取失败: {}", error),
}
// ?操作符的工作原理
fn demonstrate_question_mark_mechanics() -> Result<i32, ParseIntError> {
// 这行代码:
let number = "42".parse::<i32>()?;
// 等价于:
// let number = match "42".parse::<i32>() {
// Ok(n) => n,
// Err(e) => return Err(e),
// };
Ok(number * 2)
}
match demonstrate_question_mark_mechanics() {
Ok(result) => println!("?操作符结果: {}", result),
Err(error) => println!("解析错误: {}", error),
}
}
fn demonstrate_early_return() {
println!("\n提前返回模式:");
fn process_data(input: &str) -> Result<i32, String> {
// 验证输入不为空
if input.is_empty() {
return Err("输入不能为空".to_string());
}
// 解析数字
let number = input.parse::<i32>()
.map_err(|_| "无效的数字格式".to_string())?;
// 验证数字范围
if number < 0 {
return Err("数字不能为负".to_string());
}
if number > 100 {
return Err("数字不能大于100".to_string());
}
// 处理数据
Ok(number * 2)
}
let test_cases = vec![
"",
"abc",
"-5",
"150",
"42",
];
for input in test_cases {
match process_data(input) {
Ok(result) => println!("输入 '{}' -> 结果: {}", input, result),
Err(error) => println!("输入 '{}' -> 错误: {}", input, error),
}
}
}
fn demonstrate_multiple_error_types() {
println!("\n多种错误类型:");
// 定义自定义错误类型
#[derive(Debug)]
enum AppError {
Io(io::Error),
Parse(ParseIntError),
Custom(String),
}
// 实现From trait进行错误转换
impl From<io::Error> for AppError {
fn from(error: io::Error) -> Self {
AppError::Io(error)
}
}
impl From<ParseIntError> for AppError {
fn from(error: ParseIntError) -> Self {
AppError::Parse(error)
}
}
// 使用统一的错误类型
fn read_and_parse_number(filename: &str) -> Result<i32, AppError> {
let contents = std::fs::read_to_string(filename)?; // io::Error -> AppError
let trimmed = contents.trim();
if trimmed.is_empty() {
return Err(AppError::Custom("文件为空".to_string()));
}
let number = trimmed.parse::<i32>()?; // ParseIntError -> AppError
Ok(number)
}
// 创建测试文件
std::fs::write("test_number.txt", "42").unwrap();
match read_and_parse_number("test_number.txt") {
Ok(number) => println!("解析的数字: {}", number),
Err(AppError::Io(error)) => println!("IO错误: {}", error),
Err(AppError::Parse(error)) => println!("解析错误: {}", error),
Err(AppError::Custom(message)) => println!("自定义错误: {}", message),
}
// 清理测试文件
let _ = std::fs::remove_file("test_number.txt");
// 使用Box<dyn Error>处理多种错误类型
fn flexible_error_handling(input: &str) -> Result<i32, Box<dyn std::error::Error>> {
let number = input.parse::<i32>()?;
if number < 0 {
return Err("负数不被允许".into());
}
Ok(number * 2)
}
match flexible_error_handling("42") {
Ok(result) => println!("灵活错误处理结果: {}", result),
Err(error) => println!("灵活错误处理失败: {}", error),
}
match flexible_error_handling("-5") {
Ok(result) => println!("灵活错误处理结果: {}", result),
Err(error) => println!("灵活错误处理失败: {}", error),
}
}
rustc error_propagation.rs && ./error_propagation
8.3 自定义错误类型
8.3.1 创建自定义错误
use std::fmt;
use std::error::Error;
use std::num::ParseIntError;
use std::io;
fn main() {
println!("🎨 自定义错误类型");
println!("================");
demonstrate_simple_custom_error();
demonstrate_enum_error();
demonstrate_error_trait();
demonstrate_error_chain();
}
// 简单的自定义错误
#[derive(Debug, Clone)]
struct SimpleError {
message: String,
}
impl SimpleError {
fn new(message: &str) -> Self {
SimpleError {
message: message.to_string(),
}
}
}
impl fmt::Display for SimpleError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "SimpleError: {}", self.message)
}
}
impl Error for SimpleError {}
fn demonstrate_simple_custom_error() {
println!("\n简单自定义错误:");
fn validate_age(age: i32) -> Result<i32, SimpleError> {
if age < 0 {
Err(SimpleError::new("年龄不能为负数"))
} else if age > 150 {
Err(SimpleError::new("年龄不能超过150岁"))
} else {
Ok(age)
}
}
let test_ages = vec![-5, 25, 200];
for age in test_ages {
match validate_age(age) {
Ok(valid_age) => println!("有效年龄: {}", valid_age),
Err(error) => println!("年龄验证失败: {}", error),
}
}
}
// 枚举错误类型
#[derive(Debug, Clone)]
enum ValidationError {
TooShort { min_length: usize, actual_length: usize },
TooLong { max_length: usize, actual_length: usize },
InvalidCharacter { character: char, position: usize },
Empty,
InvalidFormat(String),
}
impl fmt::Display for ValidationError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ValidationError::TooShort { min_length, actual_length } => {
write!(f, "字符串太短:需要至少{}个字符,实际{}个字符",
min_length, actual_length)
}
ValidationError::TooLong { max_length, actual_length } => {
write!(f, "字符串太长:最多{}个字符,实际{}个字符",
max_length, actual_length)
}
ValidationError::InvalidCharacter { character, position } => {
write!(f, "无效字符 '{}' 在位置 {}", character, position)
}
ValidationError::Empty => {
write!(f, "字符串不能为空")
}
ValidationError::InvalidFormat(msg) => {
write!(f, "格式无效: {}", msg)
}
}
}
}
impl Error for ValidationError {}
fn demonstrate_enum_error() {
println!("\n枚举错误类型:");
fn validate_username(username: &str) -> Result<String, ValidationError> {
if username.is_empty() {
return Err(ValidationError::Empty);
}
if username.len() < 3 {
return Err(ValidationError::TooShort {
min_length: 3,
actual_length: username.len(),
});
}
if username.len() > 20 {
return Err(ValidationError::TooLong {
max_length: 20,
actual_length: username.len(),
});
}
for (i, ch) in username.chars().enumerate() {
if !ch.is_alphanumeric() && ch != '_' {
return Err(ValidationError::InvalidCharacter {
character: ch,
position: i,
});
}
}
if username.chars().next().unwrap().is_ascii_digit() {
return Err(ValidationError::InvalidFormat(
"用户名不能以数字开头".to_string()
));
}
Ok(username.to_string())
}
let test_usernames = vec![
"",
"ab",
"this_is_a_very_long_username_that_exceeds_limit",
"user@name",
"123user",
"valid_user",
];
for username in test_usernames {
match validate_username(username) {
Ok(valid) => println!("有效用户名: '{}'", valid),
Err(error) => println!("用户名 '{}' 验证失败: {}", username, error),
}
}
}
// 复合错误类型
#[derive(Debug)]
enum AppError {
Io(io::Error),
Parse(ParseIntError),
Validation(ValidationError),
Network { code: u16, message: String },
Database { table: String, operation: String, cause: String },
}
impl fmt::Display for AppError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
AppError::Io(err) => write!(f, "IO错误: {}", err),
AppError::Parse(err) => write!(f, "解析错误: {}", err),
AppError::Validation(err) => write!(f, "验证错误: {}", err),
AppError::Network { code, message } => {
write!(f, "网络错误 {}: {}", code, message)
}
AppError::Database { table, operation, cause } => {
write!(f, "数据库错误: 在表 '{}' 执行 '{}' 操作时失败: {}",
table, operation, cause)
}
}
}
}
impl Error for AppError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
AppError::Io(err) => Some(err),
AppError::Parse(err) => Some(err),
AppError::Validation(err) => Some(err),
_ => None,
}
}
}
// 实现From trait进行自动转换
impl From<io::Error> for AppError {
fn from(error: io::Error) -> Self {
AppError::Io(error)
}
}
impl From<ParseIntError> for AppError {
fn from(error: ParseIntError) -> Self {
AppError::Parse(error)
}
}
impl From<ValidationError> for AppError {
fn from(error: ValidationError) -> Self {
AppError::Validation(error)
}
}
fn demonstrate_error_trait() {
println!("\nError trait实现:");
fn process_user_data(data: &str) -> Result<(String, i32), AppError> {
let parts: Vec<&str> = data.split(',').collect();
if parts.len() != 2 {
return Err(AppError::Validation(ValidationError::InvalidFormat(
"数据格式应为 'username,age'".to_string()
)));
}
let username = validate_username(parts[0].trim())?;
let age = parts[1].trim().parse::<i32>()?;
if age < 0 || age > 150 {
return Err(AppError::Validation(ValidationError::InvalidFormat(
"年龄必须在0-150之间".to_string()
)));
}
Ok((username, age))
}
fn validate_username(username: &str) -> Result<String, ValidationError> {
if username.is_empty() {
return Err(ValidationError::Empty);
}
if username.len() < 3 {
return Err(ValidationError::TooShort {
min_length: 3,
actual_length: username.len(),
});
}
Ok(username.to_string())
}
let test_data = vec![
"alice,25",
"bob",
"ab,30",
"charlie,abc",
"diana,200",
];
for data in test_data {
match process_user_data(data) {
Ok((username, age)) => println!("处理成功: 用户 '{}', 年龄 {}", username, age),
Err(error) => {
println!("处理失败: {}", error);
// 显示错误链
let mut source = error.source();
while let Some(err) = source {
println!(" 原因: {}", err);
source = err.source();
}
}
}
}
}
fn demonstrate_error_chain() {
println!("\n错误链:");
fn simulate_network_request() -> Result<String, AppError> {
// 模拟网络错误
Err(AppError::Network {
code: 404,
message: "资源未找到".to_string(),
})
}
fn simulate_database_operation() -> Result<String, AppError> {
// 模拟数据库错误
Err(AppError::Database {
table: "users".to_string(),
operation: "SELECT".to_string(),
cause: "连接超时".to_string(),
})
}
fn complex_operation() -> Result<String, AppError> {
// 尝试网络请求
let _network_result = simulate_network_request()?;
// 尝试数据库操作
let _db_result = simulate_database_operation()?;
Ok("操作成功".to_string())
}
match complex_operation() {
Ok(result) => println!("复杂操作成功: {}", result),
Err(error) => {
println!("复杂操作失败: {}", error);
// 根据错误类型进行不同处理
match error {
AppError::Network { code, .. } if code == 404 => {
println!(" -> 建议: 检查请求URL是否正确");
}
AppError::Database { table, .. } => {
println!(" -> 建议: 检查数据库连接和表 '{}' 是否存在", table);
}
_ => {
println!(" -> 建议: 检查系统状态");
}
}
}
}
}
rustc custom_errors.rs && ./custom_errors
8.3.2 错误处理最佳实践
use std::error::Error;
use std::fmt;
use std::fs::File;
use std::io::{self, Read};
fn main() {
println!("🏆 错误处理最佳实践");
println!("==================");
demonstrate_error_design_principles();
demonstrate_error_context();
demonstrate_error_recovery();
demonstrate_logging_and_monitoring();
}
// 错误设计原则
fn demonstrate_error_design_principles() {
println!("\n错误设计原则:");
// 1. 使用具体的错误类型
#[derive(Debug)]
enum ConfigError {
FileNotFound(String),
InvalidFormat { line: usize, reason: String },
MissingField(String),
InvalidValue { field: String, value: String, expected: String },
}
impl fmt::Display for ConfigError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ConfigError::FileNotFound(path) => {
write!(f, "配置文件未找到: {}", path)
}
ConfigError::InvalidFormat { line, reason } => {
write!(f, "配置文件格式错误 (行 {}): {}", line, reason)
}
ConfigError::MissingField(field) => {
write!(f, "缺少必需字段: {}", field)
}
ConfigError::InvalidValue { field, value, expected } => {
write!(f, "字段 '{}' 的值 '{}' 无效,期望: {}", field, value, expected)
}
}
}
}
impl Error for ConfigError {}
// 2. 提供有用的错误信息
fn parse_config(content: &str) -> Result<Config, ConfigError> {
let mut config = Config::default();
for (line_num, line) in content.lines().enumerate() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
let parts: Vec<&str> = line.split('=').collect();
if parts.len() != 2 {
return Err(ConfigError::InvalidFormat {
line: line_num + 1,
reason: "每行应该是 'key=value' 格式".to_string(),
});
}
let key = parts[0].trim();
let value = parts[1].trim();
match key {
"port" => {
config.port = value.parse().map_err(|_| {
ConfigError::InvalidValue {
field: "port".to_string(),
value: value.to_string(),
expected: "有效的端口号 (1-65535)".to_string(),
}
})?;
if config.port == 0 || config.port > 65535 {
return Err(ConfigError::InvalidValue {
field: "port".to_string(),
value: value.to_string(),
expected: "1-65535之间的数字".to_string(),
});
}
}
"host" => {
if value.is_empty() {
return Err(ConfigError::InvalidValue {
field: "host".to_string(),
value: value.to_string(),
expected: "非空主机名".to_string(),
});
}
config.host = value.to_string();
}
"debug" => {
config.debug = value.parse().map_err(|_| {
ConfigError::InvalidValue {
field: "debug".to_string(),
value: value.to_string(),
expected: "true 或 false".to_string(),
}
})?;
}
_ => {
println!("警告: 未知配置项 '{}'", key);
}
}
}
// 验证必需字段
if config.host.is_empty() {
return Err(ConfigError::MissingField("host".to_string()));
}
Ok(config)
}
#[derive(Debug, Default)]
struct Config {
host: String,
port: u16,
debug: bool,
}
// 测试配置解析
let test_configs = vec![
"host=localhost\nport=8080\ndebug=true",
"host=\nport=8080", // 空主机名
"port=abc", // 无效端口
"host=localhost\nport=99999", // 端口超出范围
"invalid_line", // 格式错误
];
for (i, config_content) in test_configs.iter().enumerate() {
println!("\n测试配置 {}:", i + 1);
match parse_config(config_content) {
Ok(config) => println!(" 解析成功: {:?}", config),
Err(error) => println!(" 解析失败: {}", error),
}
}
}
// 错误上下文
fn demonstrate_error_context() {
println!("\n错误上下文:");
// 使用Result扩展提供上下文
trait ResultExt<T, E> {
fn with_context<F>(self, f: F) -> Result<T, ContextError<E>>
where
F: FnOnce() -> String;
}
impl<T, E> ResultExt<T, E> for Result<T, E> {
fn with_context<F>(self, f: F) -> Result<T, ContextError<E>>
where
F: FnOnce() -> String,
{
self.map_err(|error| ContextError {
context: f(),
source: error,
})
}
}
#[derive(Debug)]
struct ContextError<E> {
context: String,
source: E,
}
impl<E: fmt::Display> fmt::Display for ContextError<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {}", self.context, self.source)
}
}
impl<E: Error + 'static> Error for ContextError<E> {
fn source(&self) -> Option<&(dyn Error + 'static)> {
Some(&self.source)
}
}
// 使用上下文的函数
fn read_user_config(user_id: u32) -> Result<String, ContextError<io::Error>> {
let config_path = format!("user_{}.conf", user_id);
std::fs::read_to_string(&config_path)
.with_context(|| format!("读取用户 {} 的配置文件 '{}' 时失败", user_id, config_path))
}
fn process_user_data(user_id: u32) -> Result<String, ContextError<ContextError<io::Error>>> {
let config = read_user_config(user_id)
.with_context(|| format!("处理用户 {} 的数据时失败", user_id))?;
Ok(format!("处理了用户 {} 的配置: {} 字符", user_id, config.len()))
}
// 测试上下文错误
match process_user_data(123) {
Ok(result) => println!("处理成功: {}", result),
Err(error) => {
println!("处理失败: {}", error);
// 显示完整的错误链
let mut source = error.source();
let mut level = 1;
while let Some(err) = source {
println!(" 级别 {}: {}", level, err);
source = err.source();
level += 1;
}
}
}
}
// 错误恢复策略
fn demonstrate_error_recovery() {
println!("\n错误恢复策略:");
// 1. 重试机制
fn retry_operation<F, T, E>(mut operation: F, max_attempts: usize) -> Result<T, E>
where
F: FnMut() -> Result<T, E>,
{
let mut attempts = 0;
loop {
attempts += 1;
match operation() {
Ok(result) => return Ok(result),
Err(error) => {
if attempts >= max_attempts {
return Err(error);
}
println!(" 尝试 {} 失败,重试中...", attempts);
std::thread::sleep(std::time::Duration::from_millis(100));
}
}
}
}
// 模拟不稳定的操作
fn unstable_operation() -> Result<String, String> {
use std::sync::atomic::{AtomicUsize, Ordering};
static COUNTER: AtomicUsize = AtomicUsize::new(0);
let count = COUNTER.fetch_add(1, Ordering::SeqCst);
if count < 2 {
Err(format!("操作失败 (尝试 {})", count + 1))
} else {
Ok("操作成功".to_string())
}
}
match retry_operation(unstable_operation, 3) {
Ok(result) => println!("重试成功: {}", result),
Err(error) => println!("重试失败: {}", error),
}
// 2. 降级策略
fn get_data_with_fallback(primary: bool) -> Result<String, String> {
if primary {
get_primary_data().or_else(|_| {
println!(" 主数据源失败,使用备用数据源");
get_fallback_data()
})
} else {
get_fallback_data()
}
}
fn get_primary_data() -> Result<String, String> {
Err("主数据源不可用".to_string())
}
fn get_fallback_data() -> Result<String, String> {
Ok("备用数据".to_string())
}
match get_data_with_fallback(true) {
Ok(data) => println!("获取数据成功: {}", data),
Err(error) => println!("获取数据失败: {}", error),
}
// 3. 部分失败处理
fn process_batch(items: Vec<&str>) -> (Vec<String>, Vec<String>) {
let mut successes = Vec::new();
let mut failures = Vec::new();
for item in items {
match process_item(item) {
Ok(result) => successes.push(result),
Err(error) => failures.push(format!("处理 '{}' 失败: {}", item, error)),
}
}
(successes, failures)
}
fn process_item(item: &str) -> Result<String, String> {
if item.is_empty() {
Err("空项目".to_string())
} else if item.len() > 10 {
Err("项目太长".to_string())
} else {
Ok(format!("已处理: {}", item))
}
}
let items = vec!["item1", "", "item3", "very_long_item_name", "item5"];
let (successes, failures) = process_batch(items);
println!("批处理结果:");
println!(" 成功: {:?}", successes);
println!(" 失败: {:?}", failures);
}
// 日志和监控
fn demonstrate_logging_and_monitoring() {
println!("\n日志和监控:");
// 简单的日志记录
#[derive(Debug)]
enum LogLevel {
Error,
Warn,
Info,
Debug,
}
fn log(level: LogLevel, message: &str) {
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
println!("[{}] {:?}: {}", timestamp, level, message);
}
// 带监控的操作
fn monitored_operation(input: &str) -> Result<String, String> {
log(LogLevel::Info, &format!("开始处理输入: {}", input));
let start_time = std::time::Instant::now();
let result = if input.is_empty() {
let error = "输入为空";
log(LogLevel::Error, error);
Err(error.to_string())
} else if input.len() > 100 {
let error = "输入太长";
log(LogLevel::Warn, &format!("{}: {} 字符", error, input.len()));
Err(error.to_string())
} else {
let result = format!("处理结果: {}", input.to_uppercase());
log(LogLevel::Info, &format!("处理成功: {} 字符", input.len()));
Ok(result)
};
let duration = start_time.elapsed();
log(LogLevel::Debug, &format!("操作耗时: {:?}", duration));
result
}
// 测试监控
let test_inputs = vec![
"hello",
"",
&"x".repeat(150),
];
for input in test_inputs {
match monitored_operation(input) {
Ok(result) => println!("操作成功: {}", result),
Err(error) => println!("操作失败: {}", error),
}
println!();
}
// 错误统计
use std::collections::HashMap;
#[derive(Debug)]
struct ErrorStats {
counts: HashMap<String, usize>,
total: usize,
}
impl ErrorStats {
fn new() -> Self {
ErrorStats {
counts: HashMap::new(),
total: 0,
}
}
fn record_error(&mut self, error_type: &str) {
*self.counts.entry(error_type.to_string()).or_insert(0) += 1;
self.total += 1;
}
fn report(&self) {
println!("错误统计报告:");
println!(" 总错误数: {}", self.total);
for (error_type, count) in &self.counts {
let percentage = (*count as f64 / self.total as f64) * 100.0;
println!(" {}: {} ({:.1}%)", error_type, count, percentage);
}
}
}
let mut stats = ErrorStats::new();
// 模拟一些错误
let errors = vec![
"网络错误", "解析错误", "网络错误", "验证错误",
"网络错误", "解析错误", "数据库错误",
];
for error in errors {
stats.record_error(error);
}
stats.report();
}
rustc error_best_practices.rs && ./error_best_practices
8.4 综合示例:文件处理系统
use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::fs::{self, File};
use std::io::{self, BufRead, BufReader, Write};
use std::path::{Path, PathBuf};
fn main() {
println!("📁 文件处理系统");
println!("===============");
let mut processor = FileProcessor::new();
// 创建测试文件
setup_test_files().unwrap_or_else(|e| {
eprintln!("设置测试文件失败: {}", e);
});
// 处理文件
match processor.process_directory("test_files") {
Ok(report) => {
println!("\n处理完成!");
report.display();
}
Err(error) => {
eprintln!("处理失败: {}", error);
// 显示错误链
let mut source = error.source();
while let Some(err) = source {
eprintln!(" 原因: {}", err);
source = err.source();
}
}
}
// 清理测试文件
cleanup_test_files().unwrap_or_else(|e| {
eprintln!("清理测试文件失败: {}", e);
});
}
// 自定义错误类型
#[derive(Debug)]
enum ProcessingError {
Io(io::Error),
InvalidFormat { file: PathBuf, line: usize, reason: String },
UnsupportedFileType(String),
ProcessingFailed { file: PathBuf, reason: String },
ConfigError(String),
}
impl fmt::Display for ProcessingError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ProcessingError::Io(err) => write!(f, "IO错误: {}", err),
ProcessingError::InvalidFormat { file, line, reason } => {
write!(f, "文件 {:?} 第 {} 行格式无效: {}", file, line, reason)
}
ProcessingError::UnsupportedFileType(ext) => {
write!(f, "不支持的文件类型: {}", ext)
}
ProcessingError::ProcessingFailed { file, reason } => {
write!(f, "处理文件 {:?} 失败: {}", file, reason)
}
ProcessingError::ConfigError(msg) => {
write!(f, "配置错误: {}", msg)
}
}
}
}
impl Error for ProcessingError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
ProcessingError::Io(err) => Some(err),
_ => None,
}
}
}
impl From<io::Error> for ProcessingError {
fn from(error: io::Error) -> Self {
ProcessingError::Io(error)
}
}
// 文件处理器
struct FileProcessor {
config: ProcessingConfig,
stats: ProcessingStats,
}
#[derive(Debug)]
struct ProcessingConfig {
supported_extensions: Vec<String>,
max_file_size: u64,
output_directory: PathBuf,
}
impl Default for ProcessingConfig {
fn default() -> Self {
ProcessingConfig {
supported_extensions: vec!["txt".to_string(), "csv".to_string(), "log".to_string()],
max_file_size: 10 * 1024 * 1024, // 10MB
output_directory: PathBuf::from("processed"),
}
}
}
#[derive(Debug, Default)]
struct ProcessingStats {
files_processed: usize,
files_failed: usize,
lines_processed: usize,
errors: Vec<String>,
}
#[derive(Debug)]
struct ProcessingReport {
stats: ProcessingStats,
duration: std::time::Duration,
}
impl ProcessingReport {
fn display(&self) {
println!("处理报告:");
println!(" 处理时间: {:?}", self.duration);
println!(" 成功处理文件: {}", self.stats.files_processed);
println!(" 失败文件: {}", self.stats.files_failed);
println!(" 总处理行数: {}", self.stats.lines_processed);
if !self.stats.errors.is_empty() {
println!(" 错误列表:");
for (i, error) in self.stats.errors.iter().enumerate() {
println!(" {}: {}", i + 1, error);
}
}
let total_files = self.stats.files_processed + self.stats.files_failed;
if total_files > 0 {
let success_rate = (self.stats.files_processed as f64 / total_files as f64) * 100.0;
println!(" 成功率: {:.1}%", success_rate);
}
}
}
impl FileProcessor {
fn new() -> Self {
FileProcessor {
config: ProcessingConfig::default(),
stats: ProcessingStats::default(),
}
}
fn process_directory(&mut self, dir_path: &str) -> Result<ProcessingReport, ProcessingError> {
let start_time = std::time::Instant::now();
// 创建输出目录
fs::create_dir_all(&self.config.output_directory)?;
// 读取目录
let entries = fs::read_dir(dir_path)?;
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_file() {
match self.process_file(&path) {
Ok(_) => {
self.stats.files_processed += 1;
println!("✓ 处理成功: {:?}", path);
}
Err(error) => {
self.stats.files_failed += 1;
let error_msg = format!("处理 {:?} 失败: {}", path, error);
self.stats.errors.push(error_msg.clone());
eprintln!("✗ {}", error_msg);
}
}
}
}
let duration = start_time.elapsed();
Ok(ProcessingReport {
stats: std::mem::take(&mut self.stats),
duration,
})
}
fn process_file(&mut self, file_path: &Path) -> Result<(), ProcessingError> {
// 检查文件扩展名
let extension = file_path
.extension()
.and_then(|ext| ext.to_str())
.ok_or_else(|| ProcessingError::UnsupportedFileType("无扩展名".to_string()))?;
if !self.config.supported_extensions.contains(&extension.to_lowercase()) {
return Err(ProcessingError::UnsupportedFileType(extension.to_string()));
}
// 检查文件大小
let metadata = fs::metadata(file_path)?;
if metadata.len() > self.config.max_file_size {
return Err(ProcessingError::ProcessingFailed {
file: file_path.to_path_buf(),
reason: format!("文件太大: {} 字节", metadata.len()),
});
}
// 根据文件类型处理
match extension.to_lowercase().as_str() {
"txt" => self.process_text_file(file_path),
"csv" => self.process_csv_file(file_path),
"log" => self.process_log_file(file_path),
_ => Err(ProcessingError::UnsupportedFileType(extension.to_string())),
}
}
fn process_text_file(&mut self, file_path: &Path) -> Result<(), ProcessingError> {
let file = File::open(file_path)?;
let reader = BufReader::new(file);
let output_path = self.config.output_directory.join(
format!("{}_processed.txt",
file_path.file_stem().unwrap().to_str().unwrap())
);
let mut output_file = File::create(&output_path)?;
let mut line_count = 0;
for (line_num, line_result) in reader.lines().enumerate() {
let line = line_result?;
line_count += 1;
// 简单的文本处理:转换为大写并添加行号
let processed_line = format!("{:04}: {}\n", line_num + 1, line.to_uppercase());
output_file.write_all(processed_line.as_bytes())?;
}
self.stats.lines_processed += line_count;
Ok(())
}
fn process_csv_file(&mut self, file_path: &Path) -> Result<(), ProcessingError> {
let file = File::open(file_path)?;
let reader = BufReader::new(file);
let output_path = self.config.output_directory.join(
format!("{}_summary.txt",
file_path.file_stem().unwrap().to_str().unwrap())
);
let mut output_file = File::create(&output_path)?;
let mut line_count = 0;
let mut column_stats: HashMap<usize, usize> = HashMap::new();
for (line_num, line_result) in reader.lines().enumerate() {
let line = line_result?;
line_count += 1;
let columns: Vec<&str> = line.split(',').collect();
// 验证CSV格式
if line_num == 0 && columns.is_empty() {
return Err(ProcessingError::InvalidFormat {
file: file_path.to_path_buf(),
line: line_num + 1,
reason: "CSV文件不能为空".to_string(),
});
}
// 统计列数
*column_stats.entry(columns.len()).or_insert(0) += 1;
}
// 写入统计信息
writeln!(output_file, "CSV文件统计: {:?}", file_path)?;
writeln!(output_file, "总行数: {}", line_count)?;
writeln!(output_file, "列数分布:")?;
for (col_count, row_count) in column_stats {
writeln!(output_file, " {} 列: {} 行", col_count, row_count)?;
}
self.stats.lines_processed += line_count;
Ok(())
}
fn process_log_file(&mut self, file_path: &Path) -> Result<(), ProcessingError> {
let file = File::open(file_path)?;
let reader = BufReader::new(file);
let output_path = self.config.output_directory.join(
format!("{}_analysis.txt",
file_path.file_stem().unwrap().to_str().unwrap())
);
let mut output_file = File::create(&output_path)?;
let mut line_count = 0;
let mut error_count = 0;
let mut warning_count = 0;
let mut info_count = 0;
for line_result in reader.lines() {
let line = line_result?;
line_count += 1;
let line_lower = line.to_lowercase();
if line_lower.contains("error") {
error_count += 1;
} else if line_lower.contains("warning") || line_lower.contains("warn") {
warning_count += 1;
} else if line_lower.contains("info") {
info_count += 1;
}
}
// 写入分析结果
writeln!(output_file, "日志文件分析: {:?}", file_path)?;
writeln!(output_file, "总行数: {}", line_count)?;
writeln!(output_file, "错误行数: {}", error_count)?;
writeln!(output_file, "警告行数: {}", warning_count)?;
writeln!(output_file, "信息行数: {}", info_count)?;
let error_rate = if line_count > 0 {
(error_count as f64 / line_count as f64) * 100.0
} else {
0.0
};
writeln!(output_file, "错误率: {:.2}%", error_rate)?;
self.stats.lines_processed += line_count;
Ok(())
}
}
// 辅助函数
fn setup_test_files() -> Result<(), io::Error> {
fs::create_dir_all("test_files")?;
// 创建测试文本文件
fs::write("test_files/sample.txt", "Hello World\nThis is a test file\nWith multiple lines")?;
// 创建测试CSV文件
fs::write("test_files/data.csv", "name,age,city\nAlice,30,New York\nBob,25,London\nCharlie,35,Tokyo")?;
// 创建测试日志文件
fs::write("test_files/app.log",
"INFO: Application started\nWARNING: Low memory\nERROR: Database connection failed\nINFO: User logged in")?;
// 创建不支持的文件类型
fs::write("test_files/image.jpg", "fake image data")?;
Ok(())
}
fn cleanup_test_files() -> Result<(), io::Error> {
if Path::new("test_files").exists() {
fs::remove_dir_all("test_files")?;
}
if Path::new("processed").exists() {
fs::remove_dir_all("processed")?;
}
Ok(())
}
rustc file_processing_system.rs && ./file_processing_system
本章小结
在本章中,我们深入学习了Rust的错误处理机制:
🎯 核心概念
- 错误处理哲学: 显式、类型安全、强制处理
- Option类型: 处理可能不存在的值
- Result类型: 处理可能失败的操作
- ?操作符: 简化错误传播
🛠️ 实用技能
- 使用
match、if let、while let处理错误 - 创建自定义错误类型
- 实现错误转换和链式处理
- 应用错误恢复策略
📋 最佳实践
- 提供有意义的错误信息
- 使用具体的错误类型
- 实现适当的错误恢复机制
- 记录和监控错误
🔧 高级特性
- 错误上下文和错误链
- 重试和降级策略
- 批处理中的部分失败处理
- 错误统计和分析
下一章预告
下一章我们将学习泛型和特征,探索Rust的类型系统如何提供代码复用和抽象能力:
- 🔄 泛型函数和结构体
- 🎭 特征定义和实现
- 🔗 特征边界和关联类型
- 🏗️ 高级特征模式
- 🎯 实战:构建通用数据结构
练习题
基础练习
- 创建一个函数,安全地将字符串转换为数字,处理所有可能的错误情况
- 实现一个简单的配置文件解析器,使用自定义错误类型
- 编写一个函数,读取多个文件并收集所有成功和失败的结果
进阶练习
- 实现一个带重试机制的网络请求模拟器
- 创建一个错误恢复系统,支持多种降级策略
- 设计一个日志分析工具,统计不同类型的错误
挑战练习
- 实现一个完整的错误处理框架,包括上下文、重试、监控
- 创建一个数据验证库,支持链式验证和详细错误报告
- 设计一个分布式系统的错误处理策略,处理网络分区和服务降级
通过这些练习,你将掌握Rust错误处理的精髓,能够构建健壮可靠的应用程序!