问题描述
考虑以下示例代码:
#[derive(Clone,copy)]
#[allow(dead_code)]
enum Enum {
A { a: u8,b: u8 },B { a: u8 },C { b: u8 },}
fn foo(e: Enum) -> Option<u8> {
match e {
Enum::A { a,.. } | Enum::B { a,.. } => Some(a),// wanted: common!(Enum::A,Enum::B { a }) => Some(a),_ => None,}
}
fn main() {
let a = Enum::A { a: 1,b: 2 };
let b = Enum::B { a: 1 };
let c = Enum::C { b: 2 };
assert_eq!(foo(a),Some(1));
assert_eq!(foo(b),Some(1));
assert_eq!(foo(c),None);
}
如您所见,我想为此编写一个速记宏 (common!
)。如果 enum 字段声明变得更长(例如,更多的具有更长名称的字段),这将减少冗长。
(我想使用 |
作为分隔符,但我没有设法解析它。)
这是我对实现的尝试:
use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse::{Parse,ParseBuffer,Result},punctuated::Punctuated,spanned::Spanned,Error,Member,Pat,PatPath,PatStruct,Path,Token,};
#[proc_macro]
pub fn common(input: TokenStream) -> TokenStream {
let Syntax: Result<CommonSyntax> = syn::parse(input);
match Syntax {
Ok(CommonSyntax { types,fields }) => {
let fields_pattern = quote! {
{
#(
#fields,)*
..
}
};
let output = quote! {
#(
#types #fields_pattern
)|*
};
output.into()
}
Err(e) => e.to_compile_error().into(),}
}
struct CommonSyntax {
types: Vec<Path>,fields: Vec<Member>,}
impl Parse for CommonSyntax {
fn parse(input: &ParseBuffer) -> Result<Self> {
let mut types = Vec::new();
let mut fields = None;
let patterns: Punctuated<Pat,Token![,]> = input.parse_terminated(Pat::parse)?;
for pattern in patterns.into_iter() {
match pattern {
Pat::Path(PatPath { path,.. }) => {
types.push(path);
}
Pat::Struct(PatStruct {
path,fields: fields_punctuated,..
}) => {
if fields.is_some() {
return Err(Error::new(path.span(),"Expected only one fields pattern!"));
}
fields = Some(fields_punctuated.into_iter().map(|p| p.member).collect());
types.push(path);
}
pattern => return Err(Error::new(pattern.span(),"Expected struct pattern!")),}
}
Ok(CommonSyntax {
types,fields: fields.unwrap(),})
}
}
但这会产生以下编译错误:
error: macro expansion ignores token `|` and any following
--> wizard-common/src/lib.rs:25:17
|
25 | common!(Enum::A,| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ caused by the macro expansion here
|
= note: the usage of `common!` is likely invalid in pattern context
如果我在 panic!(output.to_string())
之前添加 output.into()
,我会在恐慌消息中得到预期的 Enum :: A { a,.. } | Enum :: B { a,.. }
。我的代码有什么问题?
依赖版本:
- syn v1.0.54
- 引用 v1.0.7
修正: 在引用生成的宏输出周围添加括号可以解决问题,但为什么呢?
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)