简体   繁体   English

如何创建proc_macro_attribute?

[英]How do I create a proc_macro_attribute?

Now that proc_macros have been stabilized , how does one create such a thing? 现在proc_macros 已稳定下来 ,如何创建这样的东西?

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? 从我所看到的,可以在fn whatsitsname(attrs: TokenStream, code: TokenStream) -> TokenStream上放置#[proc_macro_attribute]批注,但是我该如何注册? How can I add custom attributes? 如何添加自定义属性?

The Rust compiler has a fairly complete test suite . Rust编译器具有相当完整的测试套件 When looking for examples of newly-introduced features, I frequently start there: 在寻找新功能的示例时,我经常从这里开始:

$ 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 ...]

Here's a fully worked example: 这是一个完整的示例:

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

Cargo.toml 货物清单

We add a dependency on our macro-defining crate. 我们添加了对宏定义板条箱的依赖。

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

[dependencies]
my_macro = { path = "my_macro" }

src/main.rs src / main.rs

We import the attribute macro and add it to a function. 我们导入属性宏并将其添加到函数中。

#[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 my_macro / Cargo.toml

We specify crate_type as proc_macro . 我们将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 my_macro / src / lib.rs

We add #[proc_macro_attribute] to each function that should be a macro. 我们将#[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")
}

cargo run 货物运行

entering
args tokens: 3
input tokens: 7
exiting

The "hard" part is wrangling the TokenStream into something useful and then outputting something equally useful. “困难”部分是将TokenStream成有用的东西,然后输出同样有用的东西。 The crates syn and quote are the current gold standards for those two tasks. 包装箱synquote是这两项任务的当前黄金标准。 Dealing with TokenStream is covered in the macros chapter of The Rust Programming Language as well as API documentation . Rust编程语言宏一章以及API文档中 介绍了如何处理TokenStream

There's also #[proc_macro] , which takes functions of the form: 还有#[proc_macro] ,它采用以下形式的函数:

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

And can be invoked as the_name_of_the_macro!(...) . 可以作为the_name_of_the_macro!(...)调用。

If I understand RFC 1566 correctly, you: 如果我正确理解RFC 1566 ,则您:

  • Create a crate of type proc_macro , ie its Cargo.toml should contain 创建一个类型为proc_macro的板条箱,即其Cargo.toml应该包含

     [lib] proc-macro = true 
  • In that crate, create the implementation, annotated with #[proc_macro_attribute] . 在该板条箱中,创建带有#[proc_macro_attribute]注释的实现。 The #[proc_macro] for function-like macros and #[proc_macro_derive] for custom derives work the same, except they only have one TokenStream argument. #[proc_macro]函数的宏的#[proc_macro_derive]针对自定义函数的#[proc_macro_derive]的派生工作相同,只是它们只有一个TokenStream参数。 These are defined in the proc_macro crate. 这些在proc_macro板条箱中定义。

    The first token stream is the arguments in the attribute, the second is the body of the annotated item. 第一个令牌流是属性中的参数,第二个令牌流是带注释项的主体。

  • Then in the crate that wants to use the macro, simply depend on the proc_macro crate and import it with #[macro_use] attribute ( #[macro_use] extern crate …). 然后,在要使用该宏的板条箱中,只需依赖proc_macro板条箱,并使用#[macro_use]属性( #[macro_use] extern crate …)将其导入。

That should be enough. 那应该足够了。

The appendix in Book should be extended to mention the other proc macro types beyond #[proc_macro_derive] . 本书的附录应扩展为提及#[proc_macro_derive]以外的其他proc宏类型。 That it does not is probably a bug. 可能不是错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM