【问题标题】:Can a Rust macro create new identifiers?Rust 宏可以创建新的标识符吗?
【发布时间】:2015-02-09 11:22:44
【问题描述】:

我想创建一个 setter/getter 函数对,其中名称是基于共享组件自动生成的,但我找不到任何生成新名称的宏规则示例。

有没有办法生成像fn get_$iden()SomeEnum::XX_GET_$enum_iden 这样的代码?

【问题讨论】:

  • 是否有 2021 年的答案:D?

标签: macros code-generation rust


【解决方案1】:

如果您使用的是 Rust >= 1.31.0,我建议您使用我的 paste crate,它提供了一种在宏中创建串联标识符的稳定方法。

macro_rules! make_a_struct_and_getters {
    ($name:ident { $($field:ident),* }) => {
        // Define the struct. This expands to:
        //
        //     pub struct S {
        //         a: String,
        //         b: String,
        //         c: String,
        //     }
        pub struct $name {
            $(
                $field: String,
            )*
        }

        paste::item! {
            // An impl block with getters. Stuff in [<...>] is concatenated
            // together as one identifier. This expands to:
            //
            //     impl S {
            //         pub fn get_a(&self) -> &str { &self.a }
            //         pub fn get_b(&self) -> &str { &self.b }
            //         pub fn get_c(&self) -> &str { &self.c }
            //     }
            impl $name {
                $(
                    pub fn [<get_ $field>](&self) -> &str {
                        &self.$field
                    }
                )*
            }
        }
    };
}

make_a_struct_and_getters!(S { a, b, c });

【讨论】:

    【解决方案2】:

    我的mashup crate 提供了一种稳定的方法来创建适用于任何 Rust 版本 >= 1.15.0 的新标识符。


    #[macro_use]
    extern crate mashup;
    
    macro_rules! make_a_struct_and_getters {
        ($name:ident { $($field:ident),* }) => {
            // Define the struct. This expands to:
            //
            //     pub struct S {
            //         a: String,
            //         b: String,
            //         c: String,
            //     }
            pub struct $name {
                $(
                    $field: String,
                )*
            }
    
            // Use mashup to define a substitution macro `m!` that replaces every
            // occurrence of the tokens `"get" $field` in its input with the
            // concatenated identifier `get_ $field`.
            mashup! {
                $(
                    m["get" $field] = get_ $field;
                )*
            }
    
            // Invoke the substitution macro to build an impl block with getters.
            // This expands to:
            //
            //     impl S {
            //         pub fn get_a(&self) -> &str { &self.a }
            //         pub fn get_b(&self) -> &str { &self.b }
            //         pub fn get_c(&self) -> &str { &self.c }
            //     }
            m! {
                impl $name {
                    $(
                        pub fn "get" $field(&self) -> &str {
                            &self.$field
                        }
                    )*
                }
            }
        }
    }
    
    make_a_struct_and_getters!(S { a, b, c });
    

    【讨论】:

      【解决方案3】:

      不,不是 Rust 1.22。


      如果您可以使用夜间构建...

      是的:concat_idents!(get_, $iden),这样您就可以创建一个新的标识符。

      但是不行:解析器不允许到处调用宏,所以很多你可能试图这样做的地方都行不通。在这种情况下,你只能靠自己了。例如,fn concat_idents!(get_, $iden)(…) { … } 不起作用。

      【讨论】:

        【解决方案4】:

        有一个鲜为人知的 crate gensym 可以生成唯一的 UUID 名称并将它们作为第一个参数传递给宏,后跟逗号:

        macro_rules! gen_fn {
            ($a:ty, $b:ty) => {
                gensym::gensym!{ _gen_fn!{ $a, $b } }
            };
        }
        
        macro_rules! _gen_fn {
            ($gensym:ident, $a:ty, $b:ty) => {
                fn $gensym(a: $a, b: $b) {
                    unimplemented!()
                }
            };
        }
        
        mod test {
            gen_fn!{ u64, u64 }
            gen_fn!{ u64, u64 }
        }
        

        如果您只需要一个唯一的名称,并且您不在乎它是什么,那将很有用。我用它来解决一个问题,即每次调用宏都需要创建一个唯一的静态来保存一个单例结构。我不能使用粘贴,因为我没有唯一的标识符,我一开始就可以粘贴在一起。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-05-15
          • 2021-03-10
          • 2023-03-23
          • 2011-08-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多