簡體   English   中英

Rust 宏可以創建新的標識符嗎?

[英]Can a Rust macro create new identifiers?

我想創建一個 setter/getter 函數對,其中名稱是基於共享組件自動生成的,但我找不到任何生成新名稱的宏規則示例。

有沒有辦法生成像fn get_$iden() SomeEnum::XX_GET_$enum_iden fn get_$iden()SomeEnum::XX_GET_$enum_iden

我的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 });

如果您使用 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 });

不,不是從 Rust 1.22 開始的。


如果您可以使用每晚構建...

是的: concat_idents!(get_, $iden)等將允許您創建一個新的標識符。

但是不行:解析器不允許在任何地方進行宏調用,因此您可能試圖在很多地方執行此操作都行不通。 在這種情況下,您很遺憾只能靠自己。 fn concat_idents!(get_, $iden)(…) { … } ,例如,不會工作。

有一個鮮為人知的 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 }
}

如果您只需要一個唯一的名稱,而您並不關心它是什么,那么這可能很有用。 我用它來解決一個問題,即每次調用宏都需要創建一個唯一的靜態來保存單例結構。 我不能使用粘貼,因為我沒有唯一的標識符,我可以首先將其粘貼在一起。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM