类属性宏与自定义派生宏相似,不同于为 derive 属性生成代码,它们允许你创建新的属性。它们也更为

灵活;derive 只能用于结构体和枚举;属性还可以用于其它的项,比如函数。作为一个使用类属性宏的

例子,可以创建一个名为 route 的属性用于注解 web 应用程序框架(web application framework)的

函数:

#[route(GET, "/")]

fn index() {

#[route] 属性将由框架本身定义为一个过程宏。其宏定义的函数签名看起来像这样:

#[proc_macro_attribute]

pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {

这里有两个 TokenStream 类型的参数;第一个用于属性内容本身,也就是 GET, ”∕” 部分。第二个是属

性所标记的项:在本例中,是 fn index() {} 和剩下的函数体。

除此之外,类属性宏与自定义派生宏工作方式一致:创建 proc−macro crate 类型的 crate 并实现希望生

成代码的函数!

类函数宏

类函数宏定义看起来像函数调用的宏。类似于 macro_rules!,它们比函数更灵活;例如,可以接受未知

数量的参数。然而 macro_rules! 宏只能使用之前 ” 使用 macro_rules! 的声明宏用于通用元编程” 介绍的

类匹配的语法定义。类函数宏获取 TokenStream 参数,其定义使用 Rust 代码操纵 TokenStream,就像

另两种过程宏一样。一个类函数宏例子是可以像这样被调用的 sql! 宏:

let sql = sql!(SELECT * FROM posts WHERE id=1);

这个宏会解析其中的 SQL 语句并检查其是否是句法正确的,这是比 macro_rules! 可以做到的更为复杂

的处理。sql! 宏应该被定义为如此:

#[proc_macro]

pub fn sql(input: TokenStream) -> TokenStream {

这类似于自定义派生宏的签名:获取括号中的 token,并返回希望生成的代码。

总结

好的!现在我们学习了 Rust 并不常用但在特定情况下你可能用得着的功能。我们介绍了很多复杂的主

题,这样若你在错误信息提示或阅读他人代码时遇到他们,至少可以说之前已经见过这些概念和语法了。

你可以使用本章作为一个解决方案的参考。

接下来,我们将再开始一个项目,将本书所学的所有内容付与实践!