【问题标题】:How do I create a function-like procedural macro?如何创建类似函数的过程宏?
【发布时间】:2020-03-14 06:26:55
【问题描述】:

应该如何定义a_proc_macro 使其“返回”一个 5?

fn main() {
    let a = a_proc_macro!();
    assert!(a == 5);
}

【问题讨论】:

    标签: rust rust-macros rust-proc-macros


    【解决方案1】:

    阅读The Rust Programming Language'smacros 的章节说:

    类函数宏定义看起来像函数调用的宏。类似于 macro_rules! 宏,它们比函数更灵活;例如,他们 可以接受未知数量的参数。但是,macro_rules! 宏可以是 仅使用我们在本节中讨论的类似匹配的语法定义 “Declarative Macros with macro_rules! for General Metaprogramming” 早些时候。类函数宏采用TokenStream 参数及其 定义使用 Rust 代码作为另外两个来操纵 TokenStream 过程宏的类型。类似函数的宏的一个示例是 sql! 可以这样调用的宏:

    let sql = sql!(SELECT * FROM posts WHERE id=1);
    

    这个宏会解析其中的 SQL 语句并检查它是否 语法正确,这比 macro_rules! 宏可以做到。 sql! 宏的定义如下:

    #[proc_macro]
    pub fn sql(input: TokenStream) -> TokenStream {
    

    这个定义类似于自定义派生宏的签名:我们收到 括号内的标记并返回我们想要的代码 生成。


    由于Rust 1.45,您可以调用类似函数的过程宏作为表达式。

    example
    ├── Cargo.toml
    ├── example-macro
    │   ├── Cargo.toml
    │   ├── src
    │   │   └── lib.rs
    ├── src
    │   └── main.rs
    

    Cargo.toml

    [package]
    name = "example"
    version = "0.1.0"
    edition = "2018"
    
    [dependencies]
    example-macro = { path = "example-macro" }
    

    src/main.rs

    fn main() {
        assert_eq!(example_macro::a_proc_macro!(), 5);
    }
    

    example-macro/Cargo.toml

    [package]
    name = "example-macro"
    version = "0.1.0"
    edition = "2018"
    
    [lib]
    proc-macro = true
    

    example-macro/src/lib.rs

    extern crate proc_macro;
    
    use proc_macro::TokenStream;
    
    #[proc_macro]
    pub fn a_proc_macro(_input: TokenStream) -> TokenStream {
        "5".parse().unwrap()
    }
    

    另见:

    【讨论】:

      【解决方案2】:

      在稳定的 Rust 中,直接定义类似表达式的过程宏是不可能的。如果您可以每晚使用,Shepmaster's answer 会告诉您如何使用。

      如果您处于稳定状态,您仍然可以模拟类似表达式的过程宏,如下所示:

      • 定义一个过程宏,该宏扩展为一个函数,计算结果为您要调用的表达式;
      • 然后定义一个常规宏,该宏扩展为嵌入函数定义和调用的块。

      在您的情况下,您可以像这样定义程序宏:

      #[proc_macro]
      pub fn a_proc_macro_impl(_input: TokenStream) -> TokenStream {
          "fn output() -> usize { 5 }".parse().unwrap()
      }
      

      ...helper macro_rules! 宏遵循以下模式:

      macro_rules! a_proc_macro {
          ($($t:tt)*) => {{
              struct _X;
              impl _X {
                  a_proc_macro!($($t)*);
              }
              _X::output()
          }}
      }
      

      这是一个 hack,而且很麻烦,但是proc-macro-hack crate 可以帮助您使用上述技术生成过程宏。在proc-macro-hack crate 的帮助下,您可以在稳定版上运行 Shepmaster 的答案中几乎未更改的代码:

      • 编辑两个Cargo.toml 文件并将proc-macro-hack = "0.5.11" 添加到依赖项部分;
      • src/main.rs 中添加#[proc_macro_hack] use example_macro::a_proc_macro;,并从本地命名空间调用a_proc_macro!
      • example-macro/src/lib.rsa_proc_macro的定义前添加#[proc_macro_hack::proc_macro_hack]

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-12-05
        • 1970-01-01
        • 2020-03-14
        • 1970-01-01
        • 1970-01-01
        • 2017-01-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多