如何创建类似函数的过程宏?
How do I create a function-like procedural macro?
a_proc_macro
应该如何定义才能 "returns" 5?
fn main() {
let a = a_proc_macro!();
assert!(a == 5);
}
阅读 The Rust Programming Language's chapter on macros 说:
Function-like macros define macros that look like function calls. Similarly to
macro_rules!
macros, they’re more flexible than functions; for example, they
can take an unknown number of arguments. However, macro_rules!
macros can be
defined only using the match-like syntax we discussed in the section
“Declarative Macros with macro_rules!
for General Metaprogramming”
earlier. Function-like macros take a TokenStream
parameter and their
definition manipulates that TokenStream
using Rust code as the other two
types of procedural macros do. An example of a function-like macro is an sql!
macro that might be called like so:
let sql = sql!(SELECT * FROM posts WHERE id=1);
This macro would parse the SQL statement inside it and check that it’s
syntactically correct, which is much more complex processing than a
macro_rules!
macro can do. The sql!
macro would be defined like this:
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
This definition is similar to the custom derive macro’s signature: we receive
the tokens that are inside the parentheses and return the code we wanted to
generate.
自 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()
}
另请参阅:
在稳定的 Rust 中尚不能直接定义类似表达式的过程宏。如果你可以每晚使用, 显示如何。
如果你是稳定的,你仍然可以模拟类似表达式的过程宏,如下所示:
- 定义一个过程宏,该宏扩展为一个函数,该函数的计算结果为您要调用的表达式;
- 然后定义一个扩展为嵌入函数定义和调用的块的常规宏。
在您的情况下,您将像这样定义程序宏:
#[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 crate 可以解决这个问题,并且可以更轻松地使用上述技术生成过程宏。在 proc-macro-hack
crate 的帮助下,您可以 运行 来自 Shepmaster 在 stable 上的回答的几乎不变的代码:
- 编辑两个
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.rs
中的a_proc_macro
定义前添加#[proc_macro_hack::proc_macro_hack]
。
a_proc_macro
应该如何定义才能 "returns" 5?
fn main() {
let a = a_proc_macro!();
assert!(a == 5);
}
阅读 The Rust Programming Language's chapter on macros 说:
Function-like macros define macros that look like function calls. Similarly to
macro_rules!
macros, they’re more flexible than functions; for example, they can take an unknown number of arguments. However,macro_rules!
macros can be defined only using the match-like syntax we discussed in the section “Declarative Macros withmacro_rules!
for General Metaprogramming” earlier. Function-like macros take aTokenStream
parameter and their definition manipulates thatTokenStream
using Rust code as the other two types of procedural macros do. An example of a function-like macro is ansql!
macro that might be called like so:let sql = sql!(SELECT * FROM posts WHERE id=1);
This macro would parse the SQL statement inside it and check that it’s syntactically correct, which is much more complex processing than a
macro_rules!
macro can do. Thesql!
macro would be defined like this:#[proc_macro] pub fn sql(input: TokenStream) -> TokenStream {
This definition is similar to the custom derive macro’s signature: we receive the tokens that are inside the parentheses and return the code we wanted to generate.
自 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()
}
另请参阅:
在稳定的 Rust 中尚不能直接定义类似表达式的过程宏。如果你可以每晚使用,
如果你是稳定的,你仍然可以模拟类似表达式的过程宏,如下所示:
- 定义一个过程宏,该宏扩展为一个函数,该函数的计算结果为您要调用的表达式;
- 然后定义一个扩展为嵌入函数定义和调用的块的常规宏。
在您的情况下,您将像这样定义程序宏:
#[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 crate 可以解决这个问题,并且可以更轻松地使用上述技术生成过程宏。在 proc-macro-hack
crate 的帮助下,您可以 运行 来自 Shepmaster 在 stable 上的回答的几乎不变的代码:
- 编辑两个
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.rs
中的a_proc_macro
定义前添加#[proc_macro_hack::proc_macro_hack]
。