Can a Rust macro create new identifiers?

2020-01-25 02:21发布

问题:

I'd like to create a setter/getter pair of functions where the names are automatically generated based on a shared component, but I couldn't find any example of macro rules generating a new name.

Is there a way to generate code like fn get_$iden() and SomeEnum::XX_GET_$enum_iden?

回答1:

My mashup crate provides a stable way to create new identifiers that works with any Rust version >= 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 });


回答2:

No, not as of Rust 1.22.


If you can use nightly builds...

Yes: concat_idents!(get_, $iden) and such will allow you to create a new identifier.

But no: the parser doesn't allow macro calls everywhere, so many of the places you might have sought to do this won't work. In such cases, you are sadly on your own. fn concat_idents!(get_, $iden)(…) { … }, for example, won't work.