wye*_*r33 6 macros rust rust-macros
使用 Rust 时处理派生宏中的辅助属性的简化方法是什么?为了说明我正在寻找的内容,我定义了一个名为 的派生宏Duplicate,它创建一个新结构,其中包含旧结构中已由辅助属性标记的所有元素。基本上就是这样转
#[derive(Duplicate)]
struct MyStruct {
#[dupe_me]
x : Vec <f64>,
y : bool,
#[dupe_me]
z : char,
}
Run Code Online (Sandbox Code Playgroud)
进入
#[derive(Debug)]
struct MyStructDuplicated {
x : Vec <f64>,
z : char,
}
Run Code Online (Sandbox Code Playgroud)
代码的结构是
./mymacro/src/lib.rs
./mymacro/Cargo.toml
./mybin/src/main.rs
./mybin/Cargo.toml
Run Code Online (Sandbox Code Playgroud)
与mymacro/Cargo.toml作为
[package]
name = "mymacro"
version = "0.1.0"
authors = ["blank"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
syn = "1.0.11"
quote = "1.0.2"
proc-macro2 = "1.0.6"
Run Code Online (Sandbox Code Playgroud)
并lib.rs作为
[package]
name = "mymacro"
version = "0.1.0"
authors = ["blank"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
syn = "1.0.11"
quote = "1.0.2"
proc-macro2 = "1.0.6"
Run Code Online (Sandbox Code Playgroud)
那么,mybin/Cargo.toml就是
[package]
name = "mybin"
version = "0.1.0"
authors = ["blank"]
edition = "2018"
[dependencies]
mymacro = { path = "../mymacro" }
Run Code Online (Sandbox Code Playgroud)
并且main.rs是
// External dependencies
extern crate proc_macro;
use crate::proc_macro::TokenStream;
use proc_macro2;
use quote::{format_ident, quote};
use syn;
// Define a derive macro
#[proc_macro_derive(Duplicate, attributes(dupe_me))]
pub fn duplicate_macro_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast: syn::DeriveInput = syn::parse(input).unwrap();
// Grab the name of the struct
let name = &ast.ident;
// Find the name of members we need to duplicate
let mut duped: Vec<(proc_macro2::Ident, syn::Type)> = vec![];
match ast.data {
// Only process structs
syn::Data::Struct(ref data_struct) => {
// Check the kind of fields the struct contains
match data_struct.fields {
// Structs with named fields
syn::Fields::Named(ref fields_named) => {
// Iterate over the fields
for field in fields_named.named.iter() {
// Get attributes #[..] on each field
for attr in field.attrs.iter() {
// Parse the attribute
match attr.parse_meta().unwrap() {
// Find the duplicated idents
syn::Meta::Path(ref path)
if path
.get_ident()
.unwrap()
.to_string()
== "dupe_me" =>
{
// Save the duped elements
let item = field.clone();
duped.push((item.ident.unwrap(), item.ty))
}
_ => (),
}
}
}
}
// Struct with unnamed fields
_ => (),
}
}
// Panic when we don't have a struct
_ => panic!("Must be a struct"),
}
// Transform the marked elements into new struct fields
let duped = duped
.iter()
.fold(quote!(), |es, (name, ty)| quote!(#es#name : #ty,));
// Create the new structure
let myname = format_ident!("{}Duplicated", name);
let gen = quote! {
#[derive(Debug)]
struct #myname {
#duped
}
};
gen.into()
//panic!(gen.to_string());
}
Run Code Online (Sandbox Code Playgroud)
这会产生
$ ./target/debug/mybin
MyStructDuplicated { x: [1.2, 2.3], z: 'e' }
Run Code Online (Sandbox Code Playgroud)
因此,这确实可以正常工作。同时,我忽略了相当多的错误检查lib.rs,并使用了相当多的代码来深入查找帮助器属性。我有兴趣了解是否有更好的方法来生成这样的宏。