6

Now that proc_macros have been stabilized, how does one create such a thing?

From what I've seen, there's the option of putting a #[proc_macro_attribute] annotation on a fn whatsitsname(attrs: TokenStream, code: TokenStream) -> TokenStream, but how can I register it? How can I add custom attributes?

4

2 回答 2

15

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

$ 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

货运.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_typeproc_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成有用的东西,然后输出同样有用的东西。crates synquote是这两个任务的当前黄金标准。The Rust Programming Language的宏章节以及API 文档TokenStream中涵盖了处理。

还有#[proc_macro],它具有以下形式的功能:

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

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

于 2018-10-01T14:31:08.963 回答
3

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

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

    [lib]
    proc-macro = true
    
  • 在那个箱子中,创建实现,用#[proc_macro_attribute]. #[proc_macro]for 类函数宏和for 自定义派生的#[proc_macro_derive]工作方式相同,只是它们只有一个TokenStream参数。这些在proc_macrocrate 中定义。

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

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

这应该足够了。

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

于 2018-10-01T14:20:47.023 回答