Rust宏能否创建新的标识符?

18

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

是否有一种方法可以生成这样的代码:fn get_$iden()SomeEnum::XX_GET_$enum_iden


2
有2021年的答案吗? :D - David 天宇 Wong
4个回答

19
如果您正在使用 Rust >= 1.31.0,我建议使用我的paste crate,该 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 });

14

我的mashup软件包提供了一种稳定的方法来创建新标识符,适用于任何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 });

9
不,截至Rust 1.22版本还不支持。
如果你可以使用夜间构建版本...
是的: concat_idents!(get_, $iden)等将允许你创建新标识符。
但是: 解析器并不允许在任何地方调用宏,因此你可能想要尝试这样做的许多地方将无法工作。在这种情况下,你只能自己解决问题。例如,fn concat_idents!(get_, $iden)(…) { … }将无法工作。

-1

有一个鲜为人知的箱子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 }
}
如果你只需要一个独特的名称,而不在乎它是什么,那是很有用的。我曾经用它来解决一个问题,即每次调用宏时都需要创建一个唯一的静态变量来保存一个单例结构体。由于我一开始没有可粘贴在一起的唯一标识符,所以无法使用paste函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接