Skip to content

Rust 错误处理

错误处理是编程中重要的一部分,Rust 提供了强大的错误处理机制。本章节将介绍 Rust 中的错误处理方法。

错误类型

Rust 中有两种类型的错误:

  1. 可恢复错误:使用 Result<T, E> 类型表示
  2. 不可恢复错误:使用 panic! 宏处理

不可恢复错误

当发生严重错误时,使用 panic! 宏使程序崩溃:

rust
fn main() {
    // 直接调用 panic!
    panic!("Something went wrong!");
    
    // 当索引越界时,Rust 会自动 panic
    let v = vec![1, 2, 3];
    v[99]; // 索引越界,会 panic
}

可恢复错误

使用 Result<T, E> 类型处理可恢复错误:

rust
enum Result<T, E> {
    Ok(T),
    Err(E),
}

使用 Result

rust
use std::fs::File;

fn main() {
    let file = File::open("hello.txt");
    
    match file {
        Ok(f) => println!("File opened successfully"),
        Err(e) => println!("Failed to open file: {}", e),
    }
}

传播错误

使用 ? 运算符可以方便地传播错误:

rust
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    let mut file = File::open("hello.txt")?;
    let mut username = String::new();
    file.read_to_string(&mut username)?;
    Ok(username)
}

fn main() {
    match read_username_from_file() {
        Ok(username) => println!("Username: {}", username),
        Err(e) => println!("Error: {}", e),
    }
}

简化错误传播

可以使用链式调用和 ? 运算符进一步简化代码:

rust
use std::fs::File;
use std::io::{self, Read};

fn read_username_from_file() -> Result<String, io::Error> {
    File::open("hello.txt")?
        .read_to_string(&mut String::new())?
}

// 更简洁的写法
fn read_username_from_file_simple() -> Result<String, io::Error> {
    std::fs::read_to_string("hello.txt")
}

fn main() {
    match read_username_from_file() {
        Ok(username) => println!("Username: {}", username),
        Err(e) => println!("Error: {}", e),
    }
}

自定义错误类型

可以定义自己的错误类型:

rust
use std::fmt;
use std::fs::File;
use std::io::{self, Read};

// 自定义错误类型
enum CustomError {
    IoError(io::Error),
    ParseError(String),
}

// 实现 Display trait
impl fmt::Display for CustomError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            CustomError::IoError(e) => write!(f, "IO error: {}", e),
            CustomError::ParseError(s) => write!(f, "Parse error: {}", s),
        }
    }
}

// 实现 Debug trait
impl fmt::Debug for CustomError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self)
    }
}

// 实现 From trait 用于错误转换
impl From<io::Error> for CustomError {
    fn from(e: io::Error) -> Self {
        CustomError::IoError(e)
    }
}

fn read_and_parse() -> Result<i32, CustomError> {
    let content = std::fs::read_to_string("number.txt")?;
    content.parse().map_err(|e| CustomError::ParseError(e.to_string()))
}

fn main() {
    match read_and_parse() {
        Ok(number) => println!("Number: {}", number),
        Err(e) => println!("Error: {}", e),
    }
}

错误处理的最佳实践

  1. 使用 Result 处理可恢复错误:对于可能失败但可以处理的情况,使用 Result 类型。

  2. 使用 panic! 处理不可恢复错误:对于严重的、无法恢复的错误,使用 panic!

  3. 使用 ? 运算符传播错误:简化错误处理代码。

  4. 实现自定义错误类型:对于复杂的错误情况,定义自己的错误类型。

  5. 使用 anyhow:对于应用程序,可以使用 anyhow 库简化错误处理。

  6. 使用 thiserror:对于库,可以使用 thiserror 库定义错误类型。

总结

  • 不可恢复错误:使用 panic!
  • 可恢复错误:使用 Result<T, E> 类型
  • 错误传播:使用 ? 运算符
  • 自定义错误:定义自己的错误类型
  • 错误处理库anyhowthiserror

错误处理是 Rust 程序的重要组成部分。通过本章节的学习,你应该已经掌握了 Rust 中的错误处理机制,可以在你的程序中正确处理错误。