如何将标识符 (`proc_macro::Ident`) 存储为常量以避免重复?
How can I store an identifier (`proc_macro::Ident`) as a constant to avoid repeating it?
我正在编写一个过程宏,我需要多次发出一个很长的标识符(例如可能 )。我使用 quote!
创建 TokenStream
s,但我不想一遍又一遍地重复长标识符!
例如我要生成这段代码:
let very_long_ident_is_very_long_indeed = 3;
println!("{}", very_long_ident_is_very_long_indeed);
println!("twice: {}", very_long_ident_is_very_long_indeed + very_long_ident_is_very_long_indeed);
我知道我可以创建一个 Ident
并将其插入 quote!
:
let my_ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
quote! {
let #my_ident = 3;
println!("{}", #my_ident);
println!("twice: {}", #my_ident + #my_ident);
}
到目前为止一切顺利,但我需要在我的代码库中的许多函数中使用该标识符。我希望它成为一个可以随处使用的 const
。然而,这失败了:
const FOO: Ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
出现此错误:
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:5:70
|
5 | const FOO: Ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
| ^^^^^^^^^^^^^^^^^
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:5:20
|
5 | const FOO: Ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我怀疑这些功能很快就会被标记为 const
。
我可以使字符串本身成为常量:
const IDENT: &str = "very_long_ident_is_very_long_indeed";
但是无论我想在何处使用标识符,我都需要调用 Ident::new(IDENT, Span::call_site())
,这会很烦人。我只想在我的 quote!
调用中写入 #IDENT
。我能以某种方式让它发挥作用吗?
还好有办法!
通过 #
在 quote!
中的插值通过 the ToTokens
trait 起作用。任何实现该特征的东西都可以被插入。所以我们只需要创建一个可以构造成常量并实现ToTokens
的类型。该特征使用 proc-macro2
中的类型而不是标准的 proc-macro
类型。
use proc_macro2::{Ident, Span, TokenStream};
struct IdentHelper(&'static str);
impl quote::ToTokens for IdentHelper {
fn to_tokens(&self, tokens: &mut TokenStream) {
Ident::new(self.0, Span::call_site()).to_tokens(tokens)
}
}
现在您可以定义您的标识符:
const IDENT: IdentHelper = IdentHelper("very_long_ident_is_very_long_indeed");
并直接在quote!
中使用:
quote! {
let #IDENT = 3;
}
我正在编写一个过程宏,我需要多次发出一个很长的标识符(例如可能 quote!
创建 TokenStream
s,但我不想一遍又一遍地重复长标识符!
例如我要生成这段代码:
let very_long_ident_is_very_long_indeed = 3;
println!("{}", very_long_ident_is_very_long_indeed);
println!("twice: {}", very_long_ident_is_very_long_indeed + very_long_ident_is_very_long_indeed);
我知道我可以创建一个 Ident
并将其插入 quote!
:
let my_ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
quote! {
let #my_ident = 3;
println!("{}", #my_ident);
println!("twice: {}", #my_ident + #my_ident);
}
到目前为止一切顺利,但我需要在我的代码库中的许多函数中使用该标识符。我希望它成为一个可以随处使用的 const
。然而,这失败了:
const FOO: Ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
出现此错误:
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:5:70
|
5 | const FOO: Ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
| ^^^^^^^^^^^^^^^^^
error[E0015]: calls in constants are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:5:20
|
5 | const FOO: Ident = Ident::new("very_long_ident_is_very_long_indeed", Span::call_site());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
我怀疑这些功能很快就会被标记为 const
。
我可以使字符串本身成为常量:
const IDENT: &str = "very_long_ident_is_very_long_indeed";
但是无论我想在何处使用标识符,我都需要调用 Ident::new(IDENT, Span::call_site())
,这会很烦人。我只想在我的 quote!
调用中写入 #IDENT
。我能以某种方式让它发挥作用吗?
还好有办法!
通过 #
在 quote!
中的插值通过 the ToTokens
trait 起作用。任何实现该特征的东西都可以被插入。所以我们只需要创建一个可以构造成常量并实现ToTokens
的类型。该特征使用 proc-macro2
中的类型而不是标准的 proc-macro
类型。
use proc_macro2::{Ident, Span, TokenStream};
struct IdentHelper(&'static str);
impl quote::ToTokens for IdentHelper {
fn to_tokens(&self, tokens: &mut TokenStream) {
Ident::new(self.0, Span::call_site()).to_tokens(tokens)
}
}
现在您可以定义您的标识符:
const IDENT: IdentHelper = IdentHelper("very_long_ident_is_very_long_indeed");
并直接在quote!
中使用:
quote! {
let #IDENT = 3;
}