
文章目录
- [第10章 错误处理](#第10章 错误处理)
-
- [10.1 不可恢复错误与panic!](#10.1 不可恢复错误与panic!)
- [10.2 Result类型与可恢复错误](#10.2 Result类型与可恢复错误)
- [10.3 ?运算符简化错误传播](#10.3 ?运算符简化错误传播)
- [10.4 错误处理最佳实践](#10.4 错误处理最佳实践)
- 总结
第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的错误处理系统是其可靠性和安全性的重要组成部分。通过本章的学习,我们掌握了:
- 不可恢复错误(panic):用于处理程序无法恢复的严重错误
- 可恢复错误(Result):通过类型系统强制处理可能的错误情况
- 错误传播 :使用
?运算符简化错误传播代码 - 错误处理最佳实践:包括错误类型设计、分层处理、恢复策略等
Rust的错误处理哲学强调显式和编译期检查,这虽然在开始时需要更多代码,但最终会带来更健壮、更可维护的软件系统。通过合理运用这些技术,你可以构建出既安全又易于调试的Rust应用程序。
在下一章中,我们将探讨Rust的泛型、trait和生命周期系统,这些是Rust类型系统的核心特性,能够帮助我们编写灵活且高效的代码。