程序宏派生是否可以将来自其他 crate 的派生添加到它派生的结构中?
#[derive(Combined)]
struct Foo;
Run Code Online (Sandbox Code Playgroud)
#[macro_use] extern crate quote;
extern crate proc_macro2;
extern crate proc_macro;
extern crate syn;
use proc_macro::TokenStream;
#[proc_macro_derive(Combined)]
pub fn my_macro(input: TokenStream) -> TokenStream {
let input: DeriveInput = syn::parse(input).unwrap();
let ident = input.ident;
let expanded = quote! {
#[derive(Clone, Debug)]
struct #ident;
};
expanded.into()
}
Run Code Online (Sandbox Code Playgroud)
有一些次优的解决方法 - 是的!
我在实现这个时遇到的第一个问题是结构的重复定义 - 有多个定义是行不通的。为了解决这个问题,我使用了一个必须指定的自定义属性,该属性将是生成代码中结构的名称:
#![feature(custom_attribute)]
#[macro_use] extern crate quote;
extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;
use syn::DeriveInput;
use proc_macro::TokenStream;
use proc_macro2::{Ident, Span};
use syn::{Attribute, Meta, Lit};
#[proc_macro_derive(Combined)]
#[attribute(ActualName)]
pub fn my_macro(input: TokenStream) -> TokenStream {
let mut input: DeriveInput = syn::parse(input).unwrap();
for attr in input.attrs.iter().map(Attribute::interpret_meta).filter(|x| x.is_some()).map(|x| x.unwrap()) {
if &attr.name().to_string()[..] != "ActualName" { continue }
let name;
match attr {
Meta::Word(ident) => { panic!("ActualName must be a name-value pair (i.e. #[ActualName = \"hey\"])"); },
Meta::List(_) => { panic!("ActualName must be a name-value pair (i.e. #[ActualName = \"hey\"])"); },
Meta::NameValue(meta_name_value) => {
match meta_name_value.lit {
Lit::Str(lit_str) => { name = lit_str.value(); },
_ => { panic!("ActualName must be a string"); }
};
}
};
input.ident = Ident::new(&name[..], Span::call_site());
let expanded = quote! {
#[derive(Clone, Debug)]
#input
};
return expanded.into()
}
panic!("You must specify the ActualName attribute (i.e. #[Derive(Combined), ActualName = \"...\"]")
}
Run Code Online (Sandbox Code Playgroud)
将此代码放入派生箱后,以下代码示例将起作用:
#![feature(custom_attribute)]
#[macro_use]
extern crate derive_combined;
#[derive(Combined)]
#[ActualName = "Stuff"]
struct Stuff_ {
pub a: i32,
pub b: i64,
}
fn main() {
println!("{:?}", Stuff { a: 10, b: 10 }.clone());
}
Run Code Online (Sandbox Code Playgroud)
如果您对实现此有任何疑问,这是我遵循的教程。如果这没有帮助,请随时询问。
小智 6
虽然这不能方便地完成,proc_macro_derive但可以使用它来完成,proc_macro_attribute并且看到其他答案已经使用了派生属性,此解决方案可能更适合您的用例:
extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;
use proc_macro2::TokenStream;
#[proc_macro_attribute]
pub fn add_derive(_metadata: proc_macro::TokenStream, input: proc_macro::TokenStream)
-> proc_macro::TokenStream {
let input: TokenStream = input.into();
let output = quote! {
#[derive(Debug, Serialize, Deserialize, etc, ...)]
#input
};
output.into()
}
Run Code Online (Sandbox Code Playgroud)
然后使用这个宏:
#[add_derive]
pub struct TestStruct { ... }
Run Code Online (Sandbox Code Playgroud)
值得注意的是,属性宏替换了令牌流,而派生宏适用于附加到令牌流:Rust 参考:程序宏
| 归档时间: |
|
| 查看次数: |
1614 次 |
| 最近记录: |