Макросы Rust имеют гигиену, поэтому это не работает:
macro_rules! λ {
($e:expr) => {
|it| $e
}
}
fn main() {
// this looks like it should emit:
// let f = |it| it > 0
// but it's not quite that
let f = λ!(it > 0);
f(42);
}
Это не работает из-за синтаксического контекста (вероятно, для этого есть лучшая ссылка), так что it > 0
в предполагаемом теле лямбды фактически не найдет |it|
, который используется в качестве параметра лямбды.
Обычно это то, что вы хотите. Макрогигиена — отличная функция.
Однако что, если я действительно хочу написать такой анафорический макрос? Предлагает ли Rust механизм для этого, или единственное решение в Rust требует, чтобы макрос также предоставлял ident
отдельно - как в λ!(it, it > 0)
?
В данном конкретном случае это глупо, поскольку изначально это длиннее, чем обычная лямбда, но это всего лишь пример, так что терпите меня. В качестве примера другого языка программирования, Racket по умолчанию имеет гигиену, но также предоставляет синтаксический параметр (пример aif
делает это).
Сделать это можно с помощью процедурных макросов, поскольку они негигиеничны.
[package]
name = "mymacros"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
quote = "1.0.36"
syn = { version = "2.0.65", features = ["full"] }
use proc_macro::TokenStream;
use quote::quote;
use syn::Expr;
#[proc_macro]
pub fn λ(item: TokenStream) -> TokenStream {
let expr = syn::parse_macro_input!(item as Expr);
quote! { |it| #expr }.into()
}
[dependencies]
mymacros = { path = "./mymacros" }
use mymacros::λ;
fn main() {
let f = λ!(it > 0);
dbg!(f(42));
}
[src/main.rs:6:5] f(42) = true
Однако процедурные макросы сложны для понимания и могут оказаться сложными даже для простых вещей. Если вы хотите продолжать использовать декларативные макросы (т.е. macro_rules!
), то есть контейнер unhygienic2, который сделает это за вас:
macro_rules! λ {
($e:expr) => {
|it| unhygienic2::unhygienic! { $e }
};
}