Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

错误处理

组合器

Option的逻辑运算

fn main() {
    type V = Option<&'static str>;

    let n: V = None;
    let s1: V = Some("hello");
    let s2: V = Some("world");

    // or 遵循 或运算 (短路运算)
    assert_eq!(n.or(s1), s1); // None || Some1 => Some1
    assert_eq!(s1.or(s2), s1); // Some1 || Some2 => Some1
    assert_eq!(n.or(n), n); // None || None => None

    // and 遵循 且运算 (短路运算)
    assert_eq!(n.and(s1), n); // None && Some1 => None
    assert_eq!(s1.and(s2), s2); // Some1 && Some2 => Some2
    assert_eq!(n.and(n), n); // None && None => None
}

Result的逻辑运算

fn main() {
    type V = Result<&'static str, &'static str>;
    let e1: V = Err("error1");
    let e2: V = Err("error2");
    let o1: V = Ok("ok1");
    let o2: V = Ok("ok2");

    assert_eq!(e1.or(e2), e2); // Error1 || Error2 => Error2
    assert_eq!(e1.or(o1), o1);// Error1 || Ok1 => Ok1
    assert_eq!(o1.or(o2), o1);// Ok1 || Ok2 => Ok1

    assert_eq!(e1.and(e2), e1);// Error1 && Error2 => Error1
    assert_eq!(e1.and(o1), e1);// Error1 && Ok1 => Error1
    assert_eq!(o1.and(o2), o2);// Ok1 && Ok2 => Ok2
}

or_else

or逻辑相同,但参数是闭包

fn main() {
    type V = Option<&'static str>;
    let n: V = None;
    let s1: V = Some("hello");
    let s2: V = Some("world");

    // or_else 使用 || 的原因:
    // or_else 的闭包签名是 FnOnce() -> Option<T>
    // 因为 or_else 只在 None 时调用,没有值可传,所以不需要参数
    let fn_n = || None; // 闭包不需要参数
    let fn_s = || s1;

    // 与 or 相似 只不过其参数为闭包
    assert_eq!(n.or_else(fn_n), n); // None 或 || None => None
    assert_eq!(n.or_else(fn_s), s1);  // None 或 || Some1 => Some1
    assert_eq!(s1.or_else(fn_n), s1); // Some1 或 || None => Some1
    assert_eq!(s2.or_else(fn_s), s2); // Some2 或 || Some1 => Some2
}

Result同上

and_then

and逻辑相同,但参数是闭包

fn main() {
    type V = Option<&'static str>;
    let n: V = None;
    let s1: V = Some("hello");
    let s2: V = Some("world");

    // and_then 需要 |_| 的原因:
    // and_then 的闭包签名是 FnOnce(T) -> Option<U>
    // 因为 and_then 只在 Some(value) 时调用,会把 value 传给闭包
    // 即使不使用这个值,也必须接受参数(可以用 |_| 忽略)
    let fn_n = |_| None; // 闭包必须接受一个参数
    let fn_s = |_| s1;   // 即使不使用参数值,也要用 |_| 接受
    
    assert_eq!(n.and_then(fn_n), n); // None 且 |_| None => None
    assert_eq!(n.and_then(fn_s), n); // None 且 |_| Some1 => None
    assert_eq!(s1.and_then(fn_n), n); // Some1 且 |_| None => None
    assert_eq!(s2.and_then(fn_s), s1); // Some2 且 |_| Some1 => Some1
    
    // 实际使用示例:and_then 可以访问并转换 Some 中的值
    let num_str = Some("42");
    let num = num_str.and_then(|s| s.parse::<i32>().ok());
    assert_eq!(num, Some(42));
    
    // 对比:or_else 使用 || 是因为:
    // or_else 的闭包签名是 FnOnce() -> Option<T>
    // 因为 or_else 只在 None 时调用,没有值可传,所以不需要参数
}

Result同上

Optionfilter

根据条件过滤 Option 中的值 闭包签名:FnOnce(&T) -> bool

  • 如果 OptionNone,直接返回 None(不调用闭包)
  • 如果 OptionSome(value),调用闭包判断:
    • 闭包返回 true:保留,返回 Some(value)
    • 闭包返回 false:过滤掉,返回 None
fn main() {
  
    type V = Option<u8>;

    let n: V = None;
    let s1: V = Some(1);
    let s2: V = Some(2);

    // 定义过滤条件:判断是否为偶数
    let is_even = |x: &u8| x % 2 == 0;
    
    // None 值无法过滤,直接返回 None
    assert_eq!(n.filter(is_even), n); // None => 没有值可过滤 => None
    
    // Some(1) 不满足条件(不是偶数),被过滤掉,返回 None
    assert_eq!(s1.filter(is_even), n); // Some(1) => 不是偶数 => None
    
    // Some(2) 满足条件(是偶数),保留,返回 Some(2)
    assert_eq!(s2.filter(is_even), s2); // Some(2) => 是偶数 => Some(2)
    
    // 实际应用场景示例:
    // 1. 验证年龄是否满足要求
    let age = Some(25);
    let adult = age.filter(|&a| a >= 18); // 只保留 18 岁以上的
    assert_eq!(adult, Some(25));
    
    // 2. 过滤空字符串
    let name = Some("Alice");
    let non_empty = name.filter(|s| !s.is_empty());
    assert_eq!(non_empty, Some("Alice"));
    
    // 3. 链式调用:先转换再过滤
    let input = Some("42");
    let even_number = input
        .and_then(|s| s.parse::<i32>().ok())  // 先转换为数字
        .filter(|&n| n % 2 == 0);              // 再过滤偶数
    assert_eq!(even_number, Some(42));
}

ResultOptionmap

  • .map() 方法用于对容器(如 ResultOption)中的值进行操作:
    • 如果容器是有效的(如 OkSome),则执行传入的闭包,并将结果包装到新的容器中返回。
    • 如果容器是无效的(如 ErrNone),则直接返回原来的容器,不执行闭包。
fn main() {
    type V = Result<&'static str, &'static str>;
    type S = Option<u8>;

    let e1: V = Err("error1");
    let o1: V = Ok("ok1");
    let s1: S = Some(1);
    let n1: S = None;
    let fn_r = |_| "^_^";
    assert_eq!(o1.map(fn_r), Ok("^_^")); // Ok1 -> 闭包执行 —> 返回值 -> 新Ok
    assert_eq!(e1.map(fn_r), e1); // Err1  -> 闭包不执行 -> Err1

    let fn_o = |v| v + 1;
    assert_eq!(s1.map(fn_o), Some(2)); // 同上
    assert_eq!(n1.map(fn_o), n1);
}

Resultmap_err

  • 如果当前值是 Ok,则直接返回原值,不会调用闭包。
  • 如果当前值是 Err,则会将 Err 的值传递给闭包,并将闭包的返回值作为新的 Err 值。
fn main() {
    type V = Result<&'static str, &'static str>;

    let e1: V = Err("error1");
    let o1: V = Ok("ok1");
    let fn_r = |_| "^_^";

    // map_err 将Err的值修复
    assert_eq!(o1.map_err(fn_r), o1); // Ok1 -> 闭包不执行 -> Ok1
    assert_eq!(e1.map_err(fn_r), Err("^_^")); // Err1 -> 闭包指向 -> 返回闭包执行结果 -> 新Err
}

ResultOptionmap_or

  • map_ormap相似
  • map_or多了一个默认值
    • map返回的是Result<T>Option<T>
    • map_or返回T类型
    • 如果当前值是OkSome,则应用闭包,返回闭包的返回值
    • 如果当前值是ErrNone,则直接返回默认值
fn main() {
    type V = Result<&'static str, &'static str>;
    type S = Option<u8>;
    let e1: V = Err("error1");
    let o1: V = Ok("ok1");
    let s1: S = Some(1);
    let n1: S = None;
    let str1 = "^_^";
    let default_str = "default";
    let fn_r = |_| str1;

    let default_num = 0;
    let fn_o = |v| v + 1;

    assert_eq!(o1.map_or(default_str, fn_r), str1); // Ok1 -> 应用闭包 -> str1
    assert_eq!(e1.map_or(default_str, fn_r), default_str); // Err1 -> 返回默认值 -> default_str

    assert_eq!(s1.map_or(default_num, fn_o), 2); // Some1 -> 应用闭包 -> v + 1
    assert_eq!(n1.map_or(default_num, fn_o), default_num); // None -> 应用默认值 -> default_num
}

ResultOptionmap_or_else

  • map_or_elsemap_or相似
  • map_or_else的默认值为闭包
  • 判断逻辑一致
fn main() {
    type V = Result<&'static str, &'static str>;
    type S = Option<u8>;
    let e1: V = Err("error1");
    let o1: V = Ok("ok1");
    let s1: S = Some(1);
    let n1: S = None;
    let str1 = "^_^";
    let default_str = "default";
    let fn_r = |_| str1;
    let fn_default_str = |_| default_str;

    let default_num = 0;
    let fn_o = |v| v + 1;
    let fn_default_num = || default_num;

    assert_eq!(o1.map_or_else(fn_default_str, fn_r), str1); // Ok1 -> 应用第二个闭包处理值 -> 返回闭包的值
    assert_eq!(e1.map_or_else(fn_default_str, fn_r), default_str); // Err1 -> 应用第一个闭包(默认) -> 返回默认闭包的结果

    assert_eq!(s1.map_or_else(fn_default_num, fn_o), 2); // Some1 -> 应用第二个闭包处理值 -> 返回闭包的值
    assert_eq!(n1.map_or_else(fn_default_num, fn_o), default_num); // None -> 应用第二个闭包处理值 -> 返回闭包的值
}

Optionok_or

  • 可以将Option转为Result
  • 并且接收一个默认值E,当为None时返回这个Err<E>
fn main() {
    type S = Option<u8>;
    let s1: S = Some(1);
    let n1: S = None;

    let none_msg = "empty";
    assert_eq!(s1.ok_or(none_msg), Ok(1)); // Some1 -> 不应用闭包 -> 返回转换后的Ok1
    assert_eq!(n1.ok_or(none_msg), Err(none_msg)); // Nome -> 应用闭包 -> Err<应用默认值>
}

Optionok_or_else

  • ok_or相似 但默认值是闭包
  • 当为None时应用这个默认值为Err并返回
fn main() {
    type S = Option<u8>;
    let s1: S = Some(1);
    let n1: S = None;

    let none_msg = "empty";
    let fn_none_msg = || none_msg;

    assert_eq!(s1.ok_or_else(fn_none_msg), Ok(1)); // Some<T> -> 不应用闭包 -> Ok<T>
    assert_eq!(n1.ok_or_else(fn_none_msg), Err(none_msg)); // None -> 应用闭包 -> Error<闭包的返回值>
}

自定义错误类型

自定义错误类型需要实现DebugDisplay.

通过派生宏自动实现Debug

use std::fmt::{Debug, Display, Formatter};

fn main() {
    #[derive(Debug)] // 通过派生宏自动实现Debug
    struct FetchError {
        code: usize,
        message: String,
    }

    impl Display for FetchError {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            //                                  ^^^^^^^^^^^^^^^^ 注意这里不是 std::result::Result
            write!(f, "Display: FetchError error!, code {}, message {}", self.code, self.message)
        }
    }

    let result: Result<(), FetchError> = Err(FetchError {
        code: 404,
        message: "Not Found".to_string(),
    });

    println!("{:#?}", result);

    if let Err(err_msg) = result {
        println!("{}", err_msg);
    }
}

手动实现Debug

use std::fmt::{Debug, Display, Formatter};

fn main() {
    struct FetchError {
        code: usize,
        message: String,
    }

    impl Display for FetchError {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            let msg = match self.code {
                404 => "Not Found",
                _ => "Error"
            };
            write!(f, "Display: FetchError error!, code {}, message {}", self.code, msg)
        }
    }

    // 手动实现Debug
    impl Debug for FetchError {
        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
            write!(f, "Debug: FetchError {{ code: {}, message: {} }}", self.code, self.message)
        }
    }

    let result: Result<(), FetchError> = Err(FetchError {
        code: 404,
        message: "page not found".to_string(),
    });

    println!("{:#?}", result);

    if let Err(err_msg) = result {
        println!("{}", err_msg);
    }

}

将其他错误类型转为自定义错误类型

fn main() {
    #[derive(Debug)]
    struct AppError {
        from: &'static str,
        message: String,
    }
    
    // 实现From trait 将io::Error转换为AppError
    impl std::convert::From<std::io::Error> for AppError {
        fn from(value: std::io::Error) -> Self {
            AppError {
                from: "io",
                message: value.to_string(),
            }
        }
    }

    // 实现From trait 将env::VarError转换为AppError
    impl std::convert::From<std::env::VarError> for AppError {
        fn from(value: std::env::VarError) -> Self {
            AppError {
                from: "env",
                message: value.to_string(),
            }
        }
    }

    fn look_file() -> Result<(), AppError> {
        let file = std::fs::File::open("test.txt")?;
        println!("file {:?}", file);
        Ok(())
    }

    fn look_env() -> Result<(), AppError> {
        let env = std::env::var("TEST")?;
        println!("env {:?}", env);
        Ok(())
    }

    fn match_app(result: &Result<(), AppError>) {
        match result {
            Ok(()) => println!("success"),
            Err(err) => {
                match err {
                    AppError { from: "io", message } => println!("io error: {}", message),
                    AppError { from: "env", message } => println!("env error: {}", message),
                    other => println!("unknown error: {:?}", other),
                }
            },
        }
    }

    match_app(&look_file());
    match_app(&look_env());
}

错误归一化

当某个函数内部可能发生多种不同类型的错误时,将这些不同类型的错误统一转换为一个统一的错误类型,使函数只返回一个错误类型。

fn main() {
    fn happen_error() -> Result<(), std::error::Error> {
        //                          ^^^^^^^^^^^^^^^^^  std::error::Error 是一个 trait,不是具体类型
        std::fs::File::open("test.txt")?; // io::Error
        std::env::var("TEST")?; // env::VarError
        Ok(())
    }

    let result = happen_error();
    println!("happen_error {:?}", result);
}

使用Box<dyn std::error::Error>

fn main() {
    fn happen_error() -> Result<(), Box<dyn std::error::Error>> {
        std::fs::File::open("test.txt")?; // io::Error
        std::env::var("TEST")?; // env::VarError
        Ok(())
    }

    let result = happen_error();
    println!("happen_error {:?}", result);
}

使用自定义错误类型

正如将其他错误类型转为自定义错误类型 通过为自定义错误类型实现From trait,将其他错误类型转换为自定义错误类型 当使用 ? 操作符时,Rust 会:

  1. 检查是否有错误
  2. 如果有错误,自动调用 From trait 将错误类型转换为函数返回的错误类型
  3. 提前返回转换后的错误
use std::fmt::{Debug, Display, Formatter};

fn main() {
    #[derive(Debug)]
    enum AppError {
        IOError(std::io::Error),
        VarError(std::env::VarError)
    }

    // 实现From trait 将io::Error转换为AppError
    impl std::convert::From<std::io::Error> for AppError {
        fn from(value: std::io::Error) -> Self {
            AppError::IOError(value)
        }
    }

    // 实现From trait 将env::VarError转换为AppError
    impl std::convert::From<std::env::VarError> for AppError {
        fn from(value: std::env::VarError) -> Self {
            AppError::VarError(value)
        }
    }

    fn happen_error() -> Result<(), AppError> {
        std::fs::File::open("test.txt")?; // 自动调用form将io::Error转为AppError::IOError
        std::env::var("TEST")?; // 自动调用form将env::VarError转为AppError::VarError
        Ok(())
    }

    if let Err(payload) = happen_error() {
        match payload {
            AppError::IOError(err) => println!("io error: {}", err),
            AppError::VarError(err) => println!("var error: {}", err)
        }
    }
}

使用thiserror

[dependencies]
thiserror = "2.0.11"
use thiserror;
fn main() {
    #[derive(Debug, thiserror::Error)]
    enum AppError {
        #[error(transparent)]
        IOError(#[from] std::io::Error),
        #[error("var not found: {0}")]
        VarError(#[from] std::env::VarError)
    }

    fn happen_error() -> Result<(), AppError> {
        std::env::var("TEST")?; // env::VarError
        std::fs::File::open("test.txt")?; // io::Error
        Ok(())
    }
    if let Err(payload) = happen_error() {
        match payload {
            AppError::IOError(err) => println!("> io error: {}", err),
            AppError::VarError(err) => println!("> var error: {}", err)
        }
    }
}

使用anyhow

[dependencies]
anyhow="1.0.95"
use anyhow;

fn main() {
    fn happen_error() -> anyhow::Result<()> {
        std::fs::File::open("test.txt")?; // io::Error
        std::env::var("TEST")?; // env::VarError
        Ok(())
    }
    if let Err(payload) = happen_error() {
        println!("anyhow: {:?}", payload);
    }
}