跳过正文

错误处理:panic/error/Option/Result

··3444 字
Rust
目录
rust-lang - 这篇文章属于一个选集。
§ 4: 本文

panic 是 Rust 提供的异常处理机制,它打印 error message,然后开始 unwinding stack,最后退出当前thread:如果是 main thread panic,则整个程序退出,否则,如果是子线程 panic,则终止该子线程,程序不退出。

unwinding stack 过程中,Rust 会回溯调用栈,drop 所有的对象和资源,默认不打印调用栈,需要指定RUST_BACKTRACE=1 cargo run 来打印回溯的调用栈。

fn drink(beverage: &str) {
    if beverage == "lemonade" { panic!("AAAaaaaa!!!!"); }
    println!("Some refreshing {} is all I need.", beverage);
}

fn main() {
    drink("water");
    drink("lemonade");
    drink("still water");
}

panic 时是 unwind(默认)还是 abort,可以配置:

  • .cargo/config.tomlprofile.dev 中配置为 abort:
  • 通过 cargo build 或 rustc 的 -C panic=abort/unwind 参数来配置: rustc lemonade.rs -C panic=abort
[profile.dev]
panic = 'unwind'  # unwind 或 abort

对于设置的 panic 行为,代码可以使用 #[cfg(panic = "xx")] 来进行条件编译,使用 cfg!() 来进行条件判断:

// 根据 panic 设置进行条件编译
#[cfg(panic = "unwind")]
fn ah() {
    println!("Spit it out!!!!");
}

#[cfg(not(panic = "unwind"))]
fn ah() {
    println!("This is not your party. Run!!!!");
}

Option/Result 是泛型 enum 类型,支持迭代(效果就如 0 个或 1 个元素),支持 ? 操作符:

struct Person {
    job: Option<Job>,
}

#[derive(Clone, Copy)]
struct Job {
    phone_number: Option<PhoneNumber>,
}

#[derive(Clone, Copy)]
struct PhoneNumber {
    area_code: Option<u8>,
    number: u32,
}

impl Person {
    fn work_phone_area_code(&self) -> Option<u8> {
        self.job?.phone_number?.area_code // 表达式中间使用 ?, 任何 None 结果都会提前返回 None
    }
}

fn main() {
    let p = Person {
        job: Some(Job {
            phone_number: Some(PhoneNumber {
                area_code: Some(61),
                number: 439222222,
            }),
        }),
    };
    assert_eq!(p.work_phone_area_code(), Some(61));
}

1 Option
#

Option<T> 是泛型 enum 类型。

常用方法:

  • XX_or(): 为 None 时使用传入的值;
  • XX_or_else(): 为 None 时使用传入的闭包函数;
  • XX_or_default() : 为 None 时使用缺省值;
pub const fn is_some(&self) -> bool
pub const fn is_none(&self) -> bool
pub fn is_some_and(self, f: impl FnOnce(T) -> bool) -> bool

let x: Option<u32> = Some(2);
assert_eq!(x.is_some_and(|x| x > 1), true);
let x: Option<u32> = Some(0);
assert_eq!(x.is_some_and(|x| x > 1), false);

// 从 &Option<T> 转换为 Option<&T>,这样后续处理时不转移 T 所有权
pub const fn as_ref(&self) -> Option<&T>
pub fn as_mut(&mut self) -> Option<&mut T>
pub fn as_pin_ref(self: Pin<&Option<T>>) -> Option<Pin<&T>>
pub fn as_pin_mut(self: Pin<&mut Option<T>>) -> Option<Pin<&mut T>>

let text: Option<String> = Some("Hello, world!".to_string());
let text_length: Option<usize> = text.as_ref().map(|s| s.len());
let mut x = Some(2);
match x.as_mut() {
    Some(v) => *v = 42,
    None => {},
}
assert_eq!(x, Some(42));

// 返回 slice,包含对应的 Some 元素,如果为 None 则返回空 slice
pub fn as_slice(&self) -> &[T]
pub fn as_mut_slice(&mut self) -> &mut [T]

assert_eq!(
    [Some(1234).as_slice(), None.as_slice()],
    [&[1234][..], &[][..]],
);

// 返回 Some 的 T 值,如果是 None 则 panic 并打印 msg (不支持格式化)
pub fn expect(self, msg: &str) -> T

// 返回 Some 的 T 值,如果是 None 则 panic
pub fn unwrap(self) -> T
// 返回 Some 的 T 值 ,如果是 None 则使用缺省值或闭包函数的返回值
pub fn unwrap_or(self, default: T) -> T
pub fn unwrap_or_else<F>(self, f: F) -> T where F: FnOnce() -> T
pub fn unwrap_or_default(self) -> T where T: Default
pub unsafe fn unwrap_unchecked(self) -> T

// 将 Option<T> 转换为 Option<U>, 如果为 None 则返回 None
pub fn map<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> U

let maybe_some_string = Some(String::from("Hello, World!"));
let maybe_some_len = maybe_some_string.map(|s| s.len());
assert_eq!(maybe_some_len, Some(13));
let x: Option<&str> = None;
assert_eq!(x.map(|s| s.len()), None);

pub fn inspect<F>(self, f: F) -> Option<T> where F: FnOnce(&T)
let v = vec![1, 2, 3, 4, 5];
let x: Option<&usize> = v.get(3).inspect(|x| println!("got: {x}")); // prints "got: 4"
let x: Option<&usize> = v.get(5).inspect(|x| println!("got: {x}")); // prints nothing

// 返回 U 值:如果为 None 则返回 default 值, 否则对 Some 值执行 f 函数
pub fn map_or<U, F>(self, default: U, f: F) -> U where F: FnOnce(T) -> U
pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U where D: FnOnce() -> U, F: FnOnce(T) -> U

let x = Some("foo");
assert_eq!(x.map_or(42, |v| v.len()), 3);
let x: Option<&str> = None;
assert_eq!(x.map_or(42, |v| v.len()), 42);

// 将 Option 转换为 Result: Some(v) -> Ok(v), None -> Err(err)
pub fn ok_or<E>(self, err: E) -> Result<T, E>
pub fn ok_or_else<E, F>(self, err: F) -> Result<T, E> where F: FnOnce() -> E

let x = Some("foo");
assert_eq!(x.ok_or(0), Ok("foo"));
let x: Option<&str> = None;
assert_eq!(x.ok_or(0), Err(0));

pub fn as_deref(&self) -> Option<&<T as Deref>::Target> where T: Deref
pub fn as_deref_mut(&mut self) -> Option<&mut <T as Deref>::Target> where T: DerefMut

// Option 也支持迭代
pub fn iter(&self) -> Iter<'_, T>
pub fn iter_mut(&mut self) -> IterMut<'_, T>

let x = Some(4);
assert_eq!(x.iter().next(), Some(&4));
let x: Option<u32> = None;
assert_eq!(x.iter().next(), None);

// 如果 self 是 None 则返回 None, 否则返回 optb
pub fn and<U>(self, optb: Option<U>) -> Option<U>
let x = Some(2);
let y: Option<&str> = None;
assert_eq!(x.and(y), None);

let x: Option<u32> = None;
let y = Some("foo");
assert_eq!(x.and(y), None);

let x = Some(2);
let y = Some("foo");
assert_eq!(x.and(y), Some("foo"));

let x: Option<u32> = None;
let y: Option<&str> = None;
assert_eq!(x.and(y), None);

// 如果 self 是 None 则返回 None, 否则返回 f 函数的结果
pub fn and_then<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> Option<U>

fn sq_then_to_string(x: u32) -> Option<String> {
    x.checked_mul(x).map(|sq| sq.to_string())
}
assert_eq!(Some(2).and_then(sq_then_to_string), Some(4.to_string()));
assert_eq!(Some(1_000_000).and_then(sq_then_to_string), None); // overflowed!
assert_eq!(None.and_then(sq_then_to_string), None);

// 如果 self 是 None 则返回 None, 否则如果 predicate 返回 true 则返回 Some
pub fn filter<P>(self, predicate: P) -> Option<T> where P: FnOnce(&T) -> bool

fn is_even(n: &i32) -> bool { n % 2 == 0 }
assert_eq!(None.filter(is_even), None);
assert_eq!(Some(3).filter(is_even), None);
assert_eq!(Some(4).filter(is_even), Some(4));


pub fn or(self, optb: Option<T>) -> Option<T>
pub fn or_else<F>(self, f: F) -> Option<T> where F: FnOnce() -> Option<T>
pub fn xor(self, optb: Option<T>) -> Option<T>

let x = None;
let y = Some(100);
assert_eq!(x.or(y), Some(100));

let x = Some(2);
let y = Some(100);
assert_eq!(x.or(y), Some(2));

let x: Option<u32> = None;
let y = None;
assert_eq!(x.or(y), None);

// 将 value 插入 Option 返回它的 &mut, Option 原来值被 dropped
pub fn insert(&mut self, value: T) -> &mut T

let mut opt = None;
let val = opt.insert(1);
assert_eq!(*val, 1);
assert_eq!(opt.unwrap(), 1);

let val = opt.insert(2);
assert_eq!(*val, 2);
*val = 3;
assert_eq!(opt.unwrap(), 3);

// 返回 Some 值的 &mut, 否则插入 value 并返回它的 &mut
pub fn get_or_insert(&mut self, value: T) -> &mut T
pub fn get_or_insert_default(&mut self) -> &mut T where T: Default
pub fn get_or_insert_with<F>(&mut self, f: F) -> &mut T where F: FnOnce() -> T

let mut x = None;
{
    let y: &mut u32 = x.get_or_insert(5);
    assert_eq!(y, &5);
    *y = 7;
}
assert_eq!(x, Some(7));

// 从 self 中获取 Some 值, 将 self 设为 None
pub fn take(&mut self) -> Option<T>

let mut x = Some(2);
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, Some(2));

let mut x: Option<u32> = None;
let y = x.take();
assert_eq!(x, None);
assert_eq!(y, None);

// predicate 返回 true 时 take Some 的值, 将 self 设置为 None
pub fn take_if<P>(&mut self, predicate: P) -> Option<T> where P: FnOnce(&mut T) -> bool

// 用 value 替换 self 值, 返回 self 以前的值
pub fn replace(&mut self, value: T) -> Option<T>

let mut x = Some(2);
let old = x.replace(5);
assert_eq!(x, Some(5));
assert_eq!(old, Some(2));

let mut x = None;
let old = x.replace(3);
assert_eq!(x, Some(3));
assert_eq!(old, None);

// 如果 self 是 Some(s) 且 other 也是 Some(o), 则返回 Some((s, o)), 否则返回 None
pub fn zip<U>(self, other: Option<U>) -> Option<(T, U)>
pub fn zip_with<U, F, R>(self, other: Option<U>, f: F) -> Option<R> where F: FnOnce(T, U) -> R

let x = Some(1);
let y = Some("hi");
let z = None::<u8>;
assert_eq!(x.zip(y), Some((1, "hi")));
assert_eq!(x.zip(z), None);

// 从 Option<&T> 生成 Option<T>
impl<T> Option<&T>
pub fn copied(self) -> Option<T> where T: Copy
pub fn cloned(self) -> Option<T> where T: Clone

2 Result
#

Result<T, E> 是泛型 Enum 类型。

如果一个表达式返回 Result, 则忽略返回值时编译器会警告, 可以赋值给 let _ = xxx 来消除警告。

Rust 2018 版本开始, main 函数支持返回 Result, 当结果是 Err 时会打印错误消息, 程序退出:

fn main() -> Result<(), Box<dyn std::error::Error>> {
    Ok(())
}

Result 别名:

use std::fmt;

type Result<T> = std::result::Result<T, DoubleError>;

#[derive(Debug, Clone)]
struct DoubleError;

impl fmt::Display for DoubleError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "invalid first item to double")
    }
}

fn double_first(vec: Vec<&str>) -> Result<i32> {
    vec.first() // 返回 Option
        .ok_or(DoubleError) // 将 Option 转换为 Result,Option 为 None 时对应 Result 的 Err
        .and_then(|s| {
            s.parse::<i32>() // 返回 Result
                .map_err(|_| DoubleError) // Result::Err 时转换为另一个错误类型
                .map(|i| 2 * i) // Result::Ok 时做 map 处理
        })
}

// 使用自定义 Result 类型
fn print(result: Result<i32>) {
    match result {
        Ok(n) => println!("The first doubled is {}", n),
        Err(e) => println!("Error: {}", e), // 自定义的错误类型 DourbleError 实现了 Display
    }
}

Result 方法:

impl<T, E> Result<T, E>

// is_ok()/is_err() 判断 Result 的类型,但不消耗 Result
pub const fn is_ok(&self) -> bool
pub const fn is_err(&self) -> bool
pub fn is_ok_and(self, f: impl FnOnce(T) -> bool) -> bool
pub fn is_err_and(self, f: impl FnOnce(E) -> bool) -> bool

// 返回一个 Option,对于 ok() 忽略了 Err 值,对于 err() 忽略了 Ok 值;
//
// 可以用于迭代器的 filter_map()/flat_map() 方法中,这些方法使用返回 Option 的泛型函数
pub fn ok(self) -> Option<T>
pub fn err(self) -> Option<E>

// 返回 Result 的引用,不消耗 Result
pub const fn as_ref(&self) -> Result<&T, &E>
pub fn as_mut(&mut self) -> Result<&mut T, &mut E>

// 对 Ok(T) 值进行转换,返回 Result<U, E>
pub fn map<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> U
// 返回 U
pub fn map_or<U, F>(self, default: U, f: F) -> U where F: FnOnce(T) -> U
pub fn map_or_else<U, D, F>(self, default: D, f: F) -> U where D: FnOnce(E) -> U, F: FnOnce(T) -> U
// 将 Result 的 Err 映射为另一个错误类型 F
pub fn map_err<F, O>(self, op: O) -> Result<T, F> where O: FnOnce(E) -> F

// inspect() 会消耗 Result
pub fn inspect<F>(self, f: F) -> Result<T, E> where F: FnOnce(&T)
pub fn inspect_err<F>(self, f: F) -> Result<T, E> where F: FnOnce(&E)

pub fn as_deref(&self) -> Result<&<T as Deref>::Target, &E> where T: Deref
pub fn as_deref_mut(&mut self) -> Result<&mut <T as Deref>::Target, &mut E> where T: DerefMut

// Result 支持迭代
pub fn iter(&self) -> Iter<'_, T>
pub fn iter_mut(&mut self) -> IterMut<'_, T>

// 返回 T
pub fn expect(self, msg: &str) -> T where E: Debug
pub fn unwrap(self) -> T where E: Debug
pub fn unwrap_or(self, default: T) -> T
pub fn unwrap_or_default(self) -> T where T: Default
pub fn unwrap_or_else<F>(self, op: F) -> T where F: FnOnce(E) -> T

// Result 必须是 Err,返回 Err,如果是 Ok 则 panic
pub fn expect_err(self, msg: &str) -> E where T: Debug
pub fn unwrap_err(self) -> E where T: Debug

// Result 必须是 Ok,返回 Ok 值,但如果是 Err 则 panic
pub fn into_ok(self) -> T where E: Into<!>
pub fn into_err(self) -> E where T: Into<!>

pub fn and<U>(self, res: Result<U, E>) -> Result<U, E>
pub fn or<F>(self, res: Result<T, F>) -> Result<T, F>
pub fn and_then<U, F>(self, op: F) -> Result<U, E> where F: FnOnce(T) -> Result<U, E>
pub fn or_else<F, O>(self, op: O) -> Result<T, F> where O: FnOnce(E) -> Result<T, F>

pub unsafe fn unwrap_unchecked(self) -> T
pub unsafe fn unwrap_err_unchecked(self) -> E

3 Error
#

Rust 标准库提供了 std::error::Error trait。标准库中绝大部分错误类型,如 std::io::Error, std::fmt::Error 等都实现了该 trait,故可以使用 &dyn std::error::Error 来统一保存这些错误类型。

标准库为 std::error::Error 实现了到 Box<dyn Error + ‘a> 和 Box<dyn Error + Sync + Send + ‘a> 的From trait 转换实现,所以实现了 std::error:Error trait 的错误类型都可以使用 ? 转换到 Box<dyn Error + ‘a> 和 Box<dyn Error + Sync + Send + ‘a> 类型:

impl<'a, E> From<E> for Box<dyn Error + 'a> where E: Error + 'a,
impl<'a, E> From<E> for Box<dyn Error + Sync + Send + 'a> where E: Error + Send + Sync + 'a,

最佳实践:函数返回的 Error 类型使用 Box<std::error::Error + Send + Sync + 'static>

  • 加 Send + Sync + ‘static 后可以让 trait object 来跨线程返回, 例如在 aysnc spawn 场景中;

标准库为实现 std::error::Error trait 的类型实现了 ToString/Display/Debug trait, 可以用 println!() 直接打印它们:

println!("error querying the weather: {}", err);
println!("error querying the weather: {:?}", err);

use std::error;
use std::fmt;

type Result<T> = std::result::Result<T, Box<dyn error::Error>>;

// 自定义 error 类型
#[derive(Debug, Clone)]
struct EmptyVec;
// 为 error 类型实现 Display trait
impl fmt::Display for EmptyVec {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "invalid first item to double")
    }
}
// 为自定义 error 类型实现 Error trait,std::error::Error 是 marker trait
impl error::Error for EmptyVec {}
// 返回自定义 Result 类型
fn double_first(vec: Vec<&str>) -> Result<i32> {
    vec.first() // 返回 Option
        .ok_or_else(|| EmptyVec.into()) // 转换为 Result,如果是 None 则返回 EmptyVec,所以结果一定是 Ok
        .and_then(|s| { // 对 Ok 执行闭包,返回闭包结果
            s.parse::<i32>() // 返回 Result
                .map_err(|e| e.into()) // 将 Err 转换为 Box<dyn error::Error> 类型
                .map(|i| 2 * i) // 对 Ok 进行转换处理
        })
}

fn double_first_v2(vec: Vec<&str>) -> Result<i32> {
    let first = vec.first() // 返回 Option
        .ok_or(EmptyVec)?;  // 将 Option 转换为 Result,为 None 时设置 Err 为 EmptyVec
    let parsed = first.parse::<i32>()?;
    Ok(2 * parsed)
}

自定义 Error 类型:提供更丰富/个性化的上下文和出错信息:

  • 实现 fmt::Display 的 fmt() 方法;
  • 实现 std::error::Error trait( Error trait 都有默认实现,一般只需标记实现);
  • 实现 From<XX> trait, 将其它类型错误 XX 转换为自定义类型错误,一般用在 ? 操作符中。如果转换不一定成功,可以使用TryFrom<XX> trait, 它返回一个 Result 来指示是否转换成功;
use std::error;
use std::error::Error;
use std::num::ParseIntError;
use std::fmt;

type Result<T> = std::result::Result<T, DoubleError>;

// 自定义错误类型
#[derive(Debug)]
enum DoubleError {
    EmptyVec,
    Parse(ParseIntError), // 封装了其它错误类型对象
}

// 实现 Display
impl fmt::Display for DoubleError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DoubleError::EmptyVec => write!(f, "please use a vector with at least one element"),
            DoubleError::Parse(..) => write!(f, "the provided string could not be parsed as int"),
        }
    }
}

// 实现 Error,这里定义 source() 方法,用来返回更深层的错误对象(该对象类型必须已实现
// Error trait)
impl error::Error for DoubleError {
    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
        match *self {
            DoubleError::EmptyVec => None,
            DoubleError::Parse(ref e) => Some(e),
        }
    }
}

// 实现 From trait,将其它类型对象转换为自定义类型错误,后续 ? 运算符需要
impl From<ParseIntError> for DoubleError {
    fn from(err: ParseIntError) -> DoubleError {
        DoubleError::Parse(err)
    }
}

fn double_first(vec: Vec<&str>) -> Result<i32> {
    let first = vec.first() // 返回 Option
        .ok_or(DoubleError::EmptyVec)?; // 转为 Result,参数指定为 None 时对应的 Err 值
    let parsed = first.parse::<i32>()?; // 自动使用 From trait 转换错误类型
    Ok(2 * parsed)
}

fn print(result: Result<i32>) {
    match result {
        Ok(n)  => println!("The first doubled is {}", n),
        Err(e) => {
            println!("Error: {}", e);
            // 提取出更深层次的错误,如果未自定义 source() 方法,则返回 None
            if let Some(source) = e.source() {
                println!("  Caused by: {}", source);
            }
        },
    }
}

第三方 thiserror crate 为自定义 Error 类型提供了基于 derive macro 的快捷实现,可以避免上面的样板式代码:

// https://github.com/Abraxas-365/langchain-rust/blob/main/src/agent/error.rs
use thiserror::Error;

use crate::{chain::ChainError, language_models::LLMError, prompt::PromptError};

#[derive(Error, Debug)]
pub enum AgentError {
    #[error("LLM error: {0}")]
    LLMError(#[from] LLMError),

    #[error("Chain error: {0}")]
    ChainError(#[from] ChainError),

    #[error("Prompt error: {0}")]
    PromptError(#[from] PromptError),

    #[error("Tool error: {0}")]
    ToolError(String),

    #[error("Missing Object On Builder: {0}")]
    MissingObject(String),

    #[error("Missing input variable: {0}")]
    MissingInputVariable(String),

    #[error("Serde json error: {0}")]
    SerdeJsonError(#[from] serde_json::Error),

    #[error("Error: {0}")]
    OtherError(String),
}
rust-lang - 这篇文章属于一个选集。
§ 4: 本文

相关文章

不安全:unsafe
··824 字
Rust
Rust
借用:refer/borrow
··3127 字
Rust
Rust 引用类型和借用
函数、方法和闭包:function/method/closure
··7032 字
Rust
Rust 函数、方法和闭包
包和模块:package/crate/module
··2066 字
Rust
Rust 项目的包和模块组织结构