Reputation: 430671
How should a_proc_macro
be defined so it "returns" a 5?
fn main() {
let a = a_proc_macro!();
assert!(a == 5);
}
Upvotes: 13
Views: 6894
Reputation: 430671
Reading The Rust Programming Language's chapter on macros says:
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.
Since Rust 1.45, you can invoke function-like procedural macros as an expression.
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()
}
See also:
Upvotes: 12
Reputation: 154906
Directly defining expression-like procedural macros is not yet possible in stable Rust. If you can use nightly, Shepmaster's answer shows how.
If you are on stable, you can still emulate expression-like procedural macros as follows:
In your case you'd define the procedural macro like this:
#[proc_macro]
pub fn a_proc_macro_impl(_input: TokenStream) -> TokenStream {
"fn output() -> usize { 5 }".parse().unwrap()
}
...the helper macro_rules!
macro follows this pattern:
macro_rules! a_proc_macro {
($($t:tt)*) => {{
struct _X;
impl _X {
a_proc_macro!($($t)*);
}
_X::output()
}}
}
This is a hack, and a cumbersome one at that, but proc-macro-hack crate comes to the rescue and makes it easier to generate procedural macros using the above technique. With the help of the proc-macro-hack
crate, you can run the almost unchanged code from Shepmaster's answer on stable:
Cargo.toml
files and add proc-macro-hack = "0.5.11"
to the dependencies section;#[proc_macro_hack] use example_macro::a_proc_macro;
in src/main.rs
, and invoke a_proc_macro!
from the local namespace.#[proc_macro_hack::proc_macro_hack]
before the definition of a_proc_macro
in example-macro/src/lib.rs
.Upvotes: 1