22、Derive macros(派生宏)

Derive macros(派生宏)

上一节,我们为Ticket结构体实现了PartialEq这个trait,使其可以使用比较运算符(==、!=),但是,如果我们想要为大量的结构体实现这个比较基础的trait,那么我们可能要手动写大量的代码,可能重复性很高,导致代码又臭又长。

为了解决这个问题,Rust引入了Derive macros(派生宏)

解构语法

除此之外,这种方式也很难维护。如果我们为自定义的结构体添加或修改了某些字段,我们必须专门去修改PartialEq的实现,导致很难维护。

 

我们可以通过把结构体解构到它的字段(新建一个Ticket实例实现比较逻辑)来减轻这个风险:

impl PartialEq for Ticket {
    fn eq(&self, other: &Self) -> bool {
        let Ticket {
            title,
            description,
            status,
        } = self;
        // [...]
    }
}

像这样,如果我们修改了Ticket的字段,那么这里的赋值(模式匹配)就会发生报错,提醒我们去修改这里的代码。

 

我们还可以重命名结构体字段,避免变量遮蔽:

impl PartialEq for Ticket {
    fn eq(&self, other: &Self) -> bool {
        let Ticket {
            title,
            description,
            status,
        } = self;
        let Ticket {
            title: other_title,
            description: other_description,
            status: other_status,
        } = other;
        // [...]
    }
}

 

在我们的工具包中,解构是一种有用的模式,但是还有另外一种更好的方式,就是派生宏

在之前的学习中,我们遇到了一些宏:

  • 在测试实例中的assert_eq!assert!
  • 用于在控制台打印内容的println!

 

Rust的宏就是代码生成器

它们根据我们提供的输入生成新的Rust代码,生成的代码会与程序的其他部分一起编译。一些宏内置在Rust的标准库中,我们也可以编写自己的宏,但是现在先不打算学,之后再学。(The little book of Rust macrosProc macro workshop

检查

有些IDE允许我们展开宏来检查生成的代码,如果不行的话,可以使用cargo-expand

派生宏

派生宏是一种特殊的Rust宏,它被指定为结构体顶部的属性:

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

 

派生宏可以用来为自定义的类型实现一些常见的traits。上面的代码就自动为Ticket实现了Partialtrait。

展开宏之后就可以发现代码与我们写的代码基本相同

image-20250909131150159

执行了cargo expand后,可以发现,里面确实生成了和我们之前写的几乎一样的代码

 

编译器在可能的情况下尽可能推动我们派生trait

练习

// TODO: A (derivable) trait implementation is missing for this exercise to compile successfully.
//   Fix it!
//
// # `Debug` primer
//
// `Debug` returns a representation of a Rust type that's suitable for debugging (hence the name).
// `assert_eq!` requires `Ticket` to implement `Debug` because, when the assertion fails, it tries to
// print both sides of the comparison to the terminal.
// If the compared type doesn't implement `Debug`, it doesn't know how to represent them!

#[derive(/* TODO */PartialEq)]
pub struct Ticket {
    title: String,
    description: String,
    status: String,
}

impl Ticket {
    pub fn new(title: String, description: String, status: String) -> Ticket {
        Ticket {
            title,
            description,
            status,
        }
    }
}

这里是让我们为Ticket实现Debug这个派生宏,以实现Debug trait

我们直接加上Debug即可!

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

也是顺利通过了!

 

这一节的学习就先到这里了…

阅读剩余
THE END