Macros 使用宏,如何获取结构字段的唯一名称?

Macros 使用宏,如何获取结构字段的唯一名称?,macros,rust,Macros,Rust,假设我调用了一些宏: my_macro!(Blah, (a, b, c)); 它的输出如下: struct Blah { a: i32, b: i32, c: i32 } impl Blah { fn foo() -> i32 { a + b + c } } (人工示例) 这些字段对结构是私有的,但我需要允许重新定义。因此,输入 my_macro!(Blah, (a, b, c, a)); 将生成如下内容: struct Bla

假设我调用了一些宏:

my_macro!(Blah, (a, b, c));
它的输出如下:

struct Blah {
    a: i32,
    b: i32,
    c: i32
}
impl Blah {
    fn foo() -> i32 {
        a + b + c
    }
}
(人工示例)

这些字段对结构是私有的,但我需要允许重新定义。因此,输入

my_macro!(Blah, (a, b, c, a));
将生成如下内容:

struct Blah {
    a1: i32,
    b: i32,
    c: i32,
    a2: i32
}
impl Blah {
    fn foo() -> i32 {
        a1 + b + c + a2
    }
}
命名方案不需要遵循任何逻辑模式


这可能吗?

不使用编译器插件,不,我认为这是不可能的。原因有二:

  • 不能构造标识符。这里有
    concat\n标识,但由于宏的扩展方式,在这种情况下它是无用的

  • 你不能做非文字的比较。也就是说,宏无法确定它以前已经看到过一次


  • 最接近的方法是直接用一个固定的标识符列表替换所有提供的标识符,但这可能不是您想要的;在这种情况下,只需指定需要4个字段,并在不使用编译器插件的情况下生成固定大小的数组
    [i32;4]

    就更容易了,不,我认为这是不可能的。原因有二:

  • 不能构造标识符。这里有
    concat\n标识,但由于宏的扩展方式,在这种情况下它是无用的

  • 你不能做非文字的比较。也就是说,宏无法确定它以前已经看到过一次

  • 最接近的方法是直接用一个固定的标识符列表替换所有提供的标识符,但这可能不是您想要的;在这种情况下,只需指定需要4个字段,并生成一个固定大小的数组
    [i32;4]

    我的板条箱为您提供了一种展开
    我的\u宏的方法,就更容易了!(Blah,(a,b,c,a))
    进入字段
    x_a
    xx_b
    xxx_c
    xxxx_d
    ,如果命名约定对您有效。我们为每个字段添加了一个附加的
    x
    ,后跟一个下划线,后跟原始字段名,这样字段就不会以冲突的名称结尾。此方法适用于任何大于等于1.15.0的版本


    我的板条箱为您提供了一种扩展
    My_宏的方法!(Blah,(a,b,c,a))
    进入字段
    x_a
    xx_b
    xxx_c
    xxxx_d
    ,如果命名约定对您有效。我们为每个字段添加了一个附加的
    x
    ,后跟一个下划线,后跟原始字段名,这样字段就不会以冲突的名称结尾。此方法适用于任何大于等于1.15.0的版本


    #[macro_use]
    extern crate mashup;
    
    macro_rules! my_macro {
        ($name:ident, ($($field:ident),*)) => {
            my_macro_helper!($name (x) () $($field)*);
        };
    }
    
    macro_rules! my_macro_helper {
        // In the recursive case: append another `x` into our prefix.
        ($name:ident ($($prefix:tt)*) ($($past:tt)*) $next:ident $($rest:ident)*) => {
            my_macro_helper!($name ($($prefix)* x) ($($past)* [$($prefix)* _ $next]) $($rest)*);
        };
    
        // When there are no fields remaining.
        ($name:ident ($($prefix:tt)*) ($([$($field:tt)*])*)) => {
            // Use mashup to define a substitution macro `m!` that replaces every
            // occurrence of the tokens `"concat" $($field)*` in its input with the
            // resulting concatenated identifier.
            mashup! {
                $(
                    m["concat" $($field)*] = $($field)*;
                )*
            }
    
            // Invoke the substitution macro to build a struct and foo method.
            // This expands to:
            //
            //     pub struct Blah {
            //         x_a: i32,
            //         xx_b: i32,
            //         xxx_c: i32,
            //         xxxx_a: i32,
            //     }
            //
            //     impl Blah {
            //         pub fn foo(&self) -> i32 {
            //             0 + self.x_a + self.xx_b + self.xxx_c + self.xxxx_a
            //         }
            //     }
            m! {
                pub struct $name {
                    $(
                        "concat" $($field)*: i32,
                    )*
                }
    
                impl $name {
                    pub fn foo(&self) -> i32 {
                        0 $(
                            + self."concat" $($field)*
                        )*
                    }
                }
            }
        };
    }
    
    my_macro!(Blah, (a, b, c, a));
    
    fn main() {}