Rust 宏可以创建新的标识符吗?
Posted
技术标签:
【中文标题】Rust 宏可以创建新的标识符吗?【英文标题】:Can a Rust macro create new identifiers? 【发布时间】:2015-02-09 11:22:44 【问题描述】:我想创建一个 setter/getter 函数对,其中名称是基于共享组件自动生成的,但我找不到任何生成新名称的宏规则示例。
有没有办法生成像fn get_$iden()
和SomeEnum::XX_GET_$enum_iden
这样的代码?
【问题讨论】:
是否有 2021 年的答案:D? 【参考方案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
如果您只需要一个唯一的名称,并且您不在乎它是什么,那将很有用。我用它来解决一个问题,即每次调用宏都需要创建一个唯一的静态来保存一个单例结构。我不能使用粘贴,因为我没有唯一的标识符,我一开始就可以粘贴在一起。
【讨论】:
以上是关于Rust 宏可以创建新的标识符吗?的主要内容,如果未能解决你的问题,请参考以下文章