如何将标识符(`proc_macro::Ident`)存储为常量以避免重复?

2024-01-28

我正在编写一个程序宏,我需要多次发出一个非常长的标识符(可能因为卫生 https://stackoverflow.com/q/59618213/2408867, 例如)。我用quote!创造TokenStreams,但我不想一遍又一遍地重复长标识符!

例如,我想生成以下代码:

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()),这会很烦人。我只想写#IDENT in my quote!调用。我能以某种方式让它发挥作用吗?


幸运的是,有办法!

插值通过# in quote!作品通过the ToTokens trait https://docs.rs/quote/1.0.2/quote/trait.ToTokens.html。实现该特征的所有内容都可以进行插值。所以我们只需要创建一个可以构造成常量并实现的类型ToTokens。该特征使用来自的类型proc-macro2而不是标准proc-macro one.

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;
}

(完整示例 https://gist.github.com/LukasKalbertodt/90ce248857f900584b33f290c0850bee)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将标识符(`proc_macro::Ident`)存储为常量以避免重复? 的相关文章

随机推荐