[英]Can a Rust macro create new identifiers?
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.我想创建一个 setter/getter 函数对,其中名称是基于共享组件自动生成的,但我找不到任何生成新名称的宏规则示例。
Is there a way to generate code like fn get_$iden()
and SomeEnum::XX_GET_$enum_iden
?有没有办法生成像fn get_$iden()
SomeEnum::XX_GET_$enum_iden
fn get_$iden()
和SomeEnum::XX_GET_$enum_iden
?
My mashup
crate provides a stable way to create new identifiers that works with any Rust version >= 1.15.0.我的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 });
If you are using Rust >= 1.31.0 I would recommend using my paste
crate which provides a stable way to create concatenated identifiers in a macro.如果您使用 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 });
No, not as of Rust 1.22.不,不是从 Rust 1.22 开始的。
If you can use nightly builds...如果您可以使用每晚构建...
Yes: concat_idents!(get_, $iden)
and such will allow you to create a new identifier.是的: concat_idents!(get_, $iden)
等将允许您创建一个新的标识符。
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. fn concat_idents!(get_, $iden)(…) { … }
,例如,不会工作。
There's a little known crate gensym that can generate unique UUID names and pass them as the first argument to a macro, followed by a comma:有一个鲜为人知的 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 }
}
If all you need is a unique name, and you don't care what it is, that can be useful.如果您只需要一个唯一的名称,而您并不关心它是什么,那么这可能很有用。 I used it to solve a problem where each invocation of a macro needed to create a unique static to hold a singleton struct.我用它来解决一个问题,即每次调用宏都需要创建一个唯一的静态来保存单例结构。 I couldn't use paste, since I didn't have unique identifiers I could paste together in the first place.我不能使用粘贴,因为我没有唯一的标识符,我可以首先将其粘贴在一起。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.