使用 Rust 时,在派生宏中处理辅助属性的简化方法是什么?为了说明我在寻找什么,我定义了一个名为的派生宏Duplicate
,它创建一个新结构,其中包含旧结构中已由辅助属性标记的所有元素。基本上,它转
#[derive(Duplicate)]
struct MyStruct {
#[dupe_me]
x : Vec <f64>,
y : bool,
#[dupe_me]
z : char,
}
进入
#[derive(Debug)]
struct MyStructDuplicated {
x : Vec <f64>,
z : char,
}
代码的结构是
./mymacro/src/lib.rs
./mymacro/Cargo.toml
./mybin/src/main.rs
./mybin/Cargo.toml
与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"
并且lib.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());
}
那么,mybin/Cargo.toml
是
[package]
name = "mybin"
version = "0.1.0"
authors = ["blank"]
edition = "2018"
[dependencies]
mymacro = { path = "../mymacro" }
并且main.rs
是
// Debugging for the macro expansions
#![feature(trace_macros)]
trace_macros!(false);
// External dependencies
use mymacro::Duplicate;
#[derive(Duplicate)]
struct MyStruct {
#[dupe_me]
x : Vec <f64>,
y : bool,
#[dupe_me]
z : char,
}
fn main() {
let foo = MyStruct {
x : vec!(1.2,2.3),
y : true,
z : 'e',
};
let bar = MyStructDuplicated {
x : foo.x,
z : foo.z,
};
println!("{:?}",bar);
}
这产生
$ ./target/debug/mybin
MyStructDuplicated { x: [1.2, 2.3], z: 'e' }
因此,这确实可以正常工作。同时,我忽略了相当多的错误检查,lib.rs
并使用了相当多的代码来深入查找帮助器属性。我有兴趣了解是否有更好的方法来生成这样的宏。