2

使用 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并使用了相当多的代码来深入查找帮助器属性。我有兴趣了解是否有更好的方法来生成这样的宏。

4

0 回答 0