44、thiserror

thiserror

 

前面讲的那几节专门为了thiserror铺垫显得有点绕道,不是吗?但这是必要的!

让我们重回正轨:自定义错误类型和thiserror

 

自定义错误类型

 

之前已经学过了如何为自定义错误类型“手动”实现Error trait。

试想一下,我们必须为代码库中的绝大多数错误类型做这件事情的话,会有很多的boilerplate(样板)。

 

我们可以通过thiserror删除一些样板,这是一个Rust的crate,它提供了一些procedural macro(过程宏)来简化自定义错误类型的创建。

#[derive(thiserror::Error, Debug)]
enum TicketNewError {
    #[error("{0}")]
    TitleError(String),
    #[error("{0}")]
    DescriptionError(String),
}

 

我们可以编写我们自己的宏

 

到目前为止,我们看到的所有的derive macro(派生宏)都是由Rust标准库提供的。

thiserror::Error就是一个第三方derive macro(派生宏)的例子。

 

derive macro(派生宏)是procedural macro(过程宏)的一个子集,是一种在编译时生成Rust代码的方式。我现在看的这个课程不会详细介绍如何编写过程宏,但是重要的是这个编写派生宏的方式还得去学。

这是一个在更高级的Rust课程中讨论的话题。

 

自定义语法

 

每个过程宏和都可以定义它自己的语法,通常在这个crate的文档里会有解释。对于thiserror而言,我们有:

  • #[derive(thiserror::Error)]:这是为自定义错误类型派生Error trait的语法,由thiserror提供帮助。
  • #[error(“{0}”)]这是为自定义错误类型的每个变体定义Display实现的语法。当错误被display(显示)时,{0}将被变体的第0个字段所替换(在本例中是String)。

 

练习

 

这次的练习就是用thiserror重写之前的TicketNewError方法。

 

题目:

// TODO: Implement the `Error` trait for `TicketNewError` using `thiserror`.
//   We've changed the enum variants to be more specific, thus removing the need for storing
//   a `String` field into each variant.
//   You'll also have to add `thiserror` as a dependency in the `Cargo.toml` file.

/* TODO */
pub enum TicketNewError {
    /* TODO */
    TitleCannotBeEmpty,
    /* TODO */
    TitleTooLong,
    /* TODO */
    DescriptionCannotBeEmpty,
    /* TODO */
    DescriptionTooLong,
}

#[derive(Debug, PartialEq, Clone)]
pub struct Ticket {
    title: String,
    description: String,
    status: Status,
}

#[derive(Debug, PartialEq, Clone)]
pub enum Status {
    ToDo,
    InProgress { assigned_to: String },
    Done,
}

impl Ticket {
    pub fn new(
        title: String,
        description: String,
        status: Status,
    ) -> Result<Ticket, TicketNewError> {
        if title.is_empty() {
            return Err(TicketNewError::TitleCannotBeEmpty);
        }
        if title.len() > 50 {
            return Err(TicketNewError::TitleTooLong);
        }
        if description.is_empty() {
            return Err(TicketNewError::DescriptionCannotBeEmpty);
        }
        if description.len() > 500 {
            return Err(TicketNewError::DescriptionTooLong);
        }

        Ok(Ticket {
            title,
            description,
            status,
        })
    }
}

 

要改动的地方也不多,就那一个枚举类型声明那一块。

 

首先在cargo.toml文件里引入这个依赖:

image-20250917161037373

 

然后按照这节学的内容开始编写代码:

#[derive(thiserror::Error, Debug)]
pub enum TicketNewError {
    #[error("Title cannot be empty")]
    TitleCannotBeEmpty,
    #[error("Title cannot be longer than 50 bytes")]
    TitleTooLong,
    #[error("Description cannot be empty")]
    DescriptionCannotBeEmpty,
    #[error("Description cannot be longer than 500 bytes")]
    DescriptionTooLong,
}

 

不过还是必须要加Debug

image-20250917161232653

 

之后就可以了。

这几节的内容就是包管理和错误处理,对工程开发也是有很大的帮助。

 

这一节就先到这里了。

阅读剩余
THE END