如何创建类似函数的程序宏?

时间:2019-11-18 20:06:28

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

应如何定义const container = document.querySelector('#container'); const ball = document.querySelector('#ball'); let mover = 5; window.addEventListener('load', () => { ball.style.position = 'relative'; ball.style.left = 0; ball.style.top = 0; }); window.addEventListener('keydown', (e) => { switch(e.key) { case 'ArrowUp' : ball.style.top = parseInt(ball.style.top) - mover + '%'; if(ball.style.left == '100%') { alert('dont move'); } break; case 'ArrowDown' : ball.style.top = parseInt(ball.style.top) + mover + '%'; break; case 'ArrowRight' : ball.style.left = parseInt(ball.style.left) + mover + '%'; break; case 'ArrowLeft' : ball.style.left = parseInt(ball.style.left) - mover + '%'; break; } }); 使其“返回” 5?

a_proc_macro

2 个答案:

答案 0 :(得分:3)

The Rust Programming Language's上阅读macros一章说:

  

类似于函数的宏定义看起来像函数调用的宏。类似于   fn main() { let a = a_proc_macro!(); assert!(a == 5); } 宏,它们比函数更灵活;例如,他们   可以接受未知数量的参数。但是,macro_rules!宏可以是   仅使用我们在本节中讨论的类似匹配语法定义   “Declarative Macros with macro_rules! for General Metaprogramming”   较早。类似函数的宏带有一个macro_rules!参数,   定义使用Rust代码作为其他两个来操纵TokenStream   程序宏的类型。类似函数的宏的一个示例是TokenStream   可以这样称呼宏:

sql!
     

此宏将解析其中的SQL语句,并检查其是否   语法正确,这比   let sql = sql!(SELECT * FROM posts WHERE id=1); 宏可以做到。 macro_rules!宏的定义如下:

sql!
     

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


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

Cargo.toml

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

src / main.rs

[package]
name = "example"
version = "0.1.0"
edition = "2018"

[dependencies]
example-macro = { path = "example-macro" }

从Rust 1.39开始,您不能在模块的顶层(包括板条箱的根模块)的顶层调用任何类似于函数的过程宏。如果您想将其用作表达式,则需要每晚使用Rust。

example-macro / Cargo.toml

#![feature(proc_macro_hygiene)]

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

example-macro / src / lib.rs

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

[lib]
proc-macro = true

另请参阅:

答案 1 :(得分:1)

在稳定的Rust中尚无法直接定义类似于表达式的过程宏。如果您可以每晚使用,Shepmaster's answer会显示如何使用。

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

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

在您的情况下,您将这样定义程序宏:

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

...帮助程序macro_rules!宏遵循以下模式:

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

这是一个hack,也是一个繁琐的hack,但是proc-macro-hack板条箱可以提供帮助,并且可以更轻松地使用上述技术生成程序宏。借助proc-macro-hack板条箱,您可以在稳定状态下运行Shepmaster的答案中几乎不变的代码:

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