如何创建 proc_macro_attribute?

Posted

技术标签:

【中文标题】如何创建 proc_macro_attribute?【英文标题】:How do I create a proc_macro_attribute? 【发布时间】:2019-03-06 05:52:55 【问题描述】:

既然proc_macroshave been stabilized,怎么会创造出这样的东西?

据我所知,可以选择在fn whatsitsname(attrs: TokenStream, code: TokenStream) -> TokenStream 上添加#[proc_macro_attribute] 注释,但我该如何注册呢?如何添加自定义属性?

【问题讨论】:

对于它的价值,这本书包含一个section on writing procedural macros。 @SvenMarnach 这仅解释了proc_macro_derive,而不是proc_macro_attribute @lllogiq 我知道这一点,但是该链接可能对登陆这里的其他人有用。 (你的第一个问题是“如何创造这样的东西”,这已经涵盖了。) 我会说它们应该记录在由@SvenMarnach 链接的书籍附录中,如果没有,这是一个应该报告的错误在跟踪中引用问题github.com/rust-lang/rust/issues/38356(它仍然是开放的,并没有提到记录该功能,但可能应该)。 【参考方案1】:

Rust 编译器有一个相当完整的test suite。在寻找新引入功能的示例时,我经常从那里开始:

$ rg -c proc_macro_attribute
src/test/run-pass-fulldeps/auxiliary/proc_macro_def.rs:2
src/test/ui-fulldeps/auxiliary/attr_proc_macro.rs:1
[... 35 other matches ...]

这是一个完整的例子:

$ tree
.
├── Cargo.toml
├── my_macro
│   ├── Cargo.toml
│   ├── src
│   │   └── lib.rs
└── src
    └── main.rs

Cargo.toml

我们添加了对宏定义 crate 的依赖。

[package]
name = "foo"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]

[dependencies]
my_macro =  path = "my_macro" 

src/main.rs

我们导入属性宏并将其添加到函数中。

#[macro_use]
extern crate my_macro;

#[log_entry_and_exit(hello, "world")]
fn this_will_be_destroyed() -> i32 
    42


fn main() 
    dummy()

my_macro/Cargo.toml

我们将crate_type 指定为proc_macro

[package]
name = "my_macro"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]

[lib]
crate_type = ["proc-macro"]

my_macro/src/lib.rs

我们将#[proc_macro_attribute] 添加到每个应该是宏的函数中。

extern crate proc_macro;

use proc_macro::*;

#[proc_macro_attribute]
pub fn log_entry_and_exit(args: TokenStream, input: TokenStream) -> TokenStream 
    let x = format!(r#"
        fn dummy() 
            println!("entering");
            println!("args tokens: ", args);
            println!("input tokens: ", input);
            println!("exiting");
        
    "#,
            args = args.into_iter().count(),
            input = input.into_iter().count(),
    );

    x.parse().expect("Generated invalid tokens")

货物运行

entering
args tokens: 3
input tokens: 7
exiting

“困难”部分是将TokenStream 变成有用的东西,然后输出同样有用的东西。板条箱syn 和quote 是这两项任务的当前黄金标准。处理TokenStream 包含在macros chapter of The Rust Programming Language 和API documentation 中。

还有#[proc_macro],它的函数形式如下:

#[proc_macro]
pub fn the_name_of_the_macro(input: TokenStream) -> TokenStream

并且可以调用为the_name_of_the_macro!(...)

【讨论】:

【参考方案2】:

如果我对RFC 1566 的理解正确,您:

创建一个 proc_macro 类型的 crate,即它的 Cargo.toml 应该包含

[lib]
proc-macro = true

在那个 crate 中,创建实现,用 #[proc_macro_attribute] 注释。类似函数的宏的#[proc_macro] 和自定义派生的#[proc_macro_derive] 的工作方式相同,只是它们只有一个TokenStream 参数。这些在proc_macro crate 中定义。

第一个标记流是属性中的参数,第二个是注释项的主体。

然后在要使用宏的 crate 中,只需依赖 proc_macro crate 和 使用 #[macro_use] 属性 (#[macro_use] extern crate...) 导入它。

应该够了。

appendix in Book 应该被扩展以提及除#[proc_macro_derive] 之外的其他 proc 宏类型。它没有可能是一个错误。

【讨论】:

以上是关于如何创建 proc_macro_attribute?的主要内容,如果未能解决你的问题,请参考以下文章

桌面快捷方式如何创建 如何创建桌面快捷方式

如何创建线程?

要用MFC创建一个窗口,如何创建?

如何创建线程?如何保证线程安全?

1如何声明一个类?如何创建类的对象?

Linux中如何查看文件的最初创建时间