Как мне создать proc_macro_attribute? - PullRequest
0 голосов
/ 01 октября 2018

Теперь, когда proc_macros стабилизированы , как можно создать такую ​​вещь?

Из того, что я видел, есть возможность добавить аннотацию #[proc_macro_attribute]a fn whatsitsname(attrs: TokenStream, code: TokenStream) -> TokenStream, но как мне его зарегистрировать?Как я могу добавить пользовательские атрибуты?

Ответы [ 2 ]

0 голосов
/ 01 октября 2018

Компилятор 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

Cargo.toml

Мы добавляем зависимость от нашего макро-определяющего ящика.

[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 описана в главе о макросах Язык программирования Rust , а также Документация API .

Также есть #[proc_macro], который принимает функции вида:

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

и может быть вызван как the_name_of_the_macro!(...).

0 голосов
/ 01 октября 2018

Если я правильно понимаю RFC 1566 , вы:

  • Создаете ящик типа proc_macro, то есть Cargo.toml должен содержать

    [lib]
    proc-macro = true
    
  • В этом ящике создайте реализацию, помеченную #[proc_macro_attribute].#[proc_macro] для функционально-подобных макросов и #[proc_macro_derive] для пользовательских производных работают одинаково, за исключением того, что они имеют только один аргумент TokenStream.Они определены в proc_macro ящике.

    Первый поток токенов - это аргументы в атрибуте, второй - тело аннотированного элемента.

  • Тогдав ящике, который хочет использовать макрос, просто положитесь на ящик proc_macro и импортируйте его с атрибутом #[macro_use] (#[macro_use] extern crate…).

Этого должно быть достаточно.

Приложение в Книге должно быть расширено, чтобы упомянуть другие типы макросов proc помимо #[proc_macro_derive].Это, вероятно, ошибка.

...