我正在尝试创建一个名为derive_pattern 的简单库。我的最终目标是能够写出这样的东西:
#[derive(Pattern)]
struct TestStruct {
x: i32,
y: i32,
}
#[test]
fn it_works() {
let test = TestStruct { x: 5, y: 10 };
match test {
// this macro should have been created by the derive,
// and should expand to a pattern that includes all of the field names
test_struct_pattern!() => {
// in this scope x and y should be defined because the pattern bound them
assert!(x == 5);
assert!(y == 5);
}
_ => unreachable!("pattern should have matched"),
}
}
我很好奇这是否可能。到目前为止,我的尝试都失败了。在定义程序宏时,可以控制标识符的范围以绕过卫生,但据我所知,程序宏无法定义新的程序宏。因此,我的程序宏必须生成 macro_rules 宏,而这些宏最终会强制执行卫生。
这是我的程序宏定义:
use inflector::Inflector;
use proc_macro2::Span;
use quote::quote;
use syn::{Fields, FieldsNamed, Ident, ItemStruct};
#[proc_macro_derive(Pattern)]
pub fn derive_pattern(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = proc_macro2::TokenStream::from(input);
let structure: ItemStruct = syn::parse2(input).expect("derive(Pattern) only works on structs.");
let name = structure.ident;
let macro_name = Ident::new(
&(name.to_string().to_snake_case() + "_pattern"),
name.span(),
);
match &structure.fields {
Fields::Named(FieldsNamed {
brace_token: _,
named,
}) => {
let mut field_names: Vec<Ident> = named
.iter()
.map(|f| f.ident.as_ref().unwrap().clone())
.collect();
for field_name in &mut field_names {
field_name.set_span(Span::call_site()); // try (and fail) to bypass hygiene
}
let output = quote! {
macro_rules! #macro_name {
() => {
#name{ #(#field_names),* }
}
}
};
output.into()
}
_ => panic!("derive(Pattern) only supports structs with named fields"),
}
}
我已将带有完整 Cargo.toml 等的版本上传到 github:https ://github.com/jgarvin/derive_pattern
我得到的错误是在测试中的第一个断言上:
Compiling derive_pattern_test_cases v0.1.0 (/home/prophet/derive_pattern/derive_pattern_test_cases)
error[E0425]: cannot find value `x` in this scope
--> derive_pattern_test_cases/src/lib.rs:16:25
|
16 | assert!(x == 5);
| ^ not found in this scope
error[E0425]: cannot find value `y` in this scope
--> derive_pattern_test_cases/src/lib.rs:17:25
|
17 | assert!(y == 5);
| ^ not found in this scope