Как мне создать proc_macro_attribute?
Теперь, когда proc_macros
стабилизировался, как можно создать такую вещь?
Из того, что я видел, есть возможность поставить #[proc_macro_attribute]
аннотация на fn whatsitsname(attrs: TokenStream, code: TokenStream) -> TokenStream
, но как я могу это зарегистрировать? Как я могу добавить пользовательские атрибуты?
2 ответа
Компилятор 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
в нечто полезное, а затем выводить что-то одинаково полезное. Ящики син и цитата являются текущими золотыми стандартами для этих двух задач. Имея дело с TokenStream
рассматривается в приложении к макросам языка программирования Rust, а также в документации по API.
Есть также #[proc_macro]
, который принимает функции вида:
#[proc_macro]
pub fn the_name_of_the_macro(input: TokenStream) -> TokenStream
И может быть вызван как the_name_of_the_macro!(...)
,
Если я правильно понимаю 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_macro_derive]
, Это, вероятно, ошибка.