macro proc-macro-hack

Procedural macros in expression position

12 releases

0.4.1 Sep 3, 2018
0.4.0 Oct 23, 2017
0.3.3 Mar 28, 2017
0.3.0 Feb 28, 2017
0.1.0 Dec 30, 2016

#10 in Procedural macro helpers

Download history 1461/week @ 2018-08-05 1417/week @ 2018-08-12 1119/week @ 2018-08-19 1654/week @ 2018-08-26 1993/week @ 2018-09-02 2652/week @ 2018-09-09 1877/week @ 2018-09-16 1658/week @ 2018-09-23 2290/week @ 2018-09-30 2124/week @ 2018-10-07 2628/week @ 2018-10-14 1877/week @ 2018-10-21 1872/week @ 2018-10-28

5,247 downloads per month
Used in 119 crates (54 directly)

MIT/Apache

18KB
279 lines

Procedural macros in expression position

Build Status Latest Version

As of Rust 1.30, the language supports user-defined function-like procedural macros. However these can only be invoked in item position, not in statements or expressions.

This crate implements an alternative type of procedural macro that can be invoked in statement or expression position.

This approach works with any stable or nightly Rust version 1.30+.

Defining procedural macros

Two crates are required to define a procedural macro.

The implementation crate

This crate must contain nothing but procedural macros. Private helper functions and private modules are fine but nothing can be public.

> example of an implementation crate

Just like you would use a #[proc_macro] attribute to define a natively supported procedural macro, use proc-macro-hack's #[proc_macro_hack] attribute to define a procedural macro that works in expression position. The function signature is the same as for ordinary function-like procedural macros.

extern crate proc_macro;
extern crate proc_macro_hack;
extern crate quote;
extern crate syn;

use proc_macro::TokenStream;
use proc_macro_hack::proc_macro_hack;
use quote::quote;
use syn::{parse_macro_input, Expr};

/// Add one to an expression.
#[proc_macro_hack]
pub fn add_one(input: TokenStream) -> TokenStream {
    let expr = parse_macro_input!(input as Expr);
    TokenStream::from(quote! {
        1 + (#expr)
    })
}

The declaration crate

This crate is allowed to contain other public things if you need, for example traits or functions or ordinary macros.

> example of a declaration crate

Within the declaration crate there needs to be a re-export of your procedural macro from the implementation crate. The re-export also carries a #[proc_macro_hack] attribute.

extern crate demo_hack_impl;
extern crate proc_macro_hack;

use proc_macro_hack::proc_macro_hack;

/// Add one to an expression.
#[proc_macro_hack]
pub use demo_hack_impl::add_one;

Both crates depend on proc-macro-hack:

[dependencies]
proc-macro-hack = "0.5"

Additionally, your implementation crate (but not your declaration crate) is a proc macro crate:

[lib]
proc-macro = true

Using procedural macros

Users of your crate depend on your declaration crate (not your implementation crate), then use your procedural macros as usual.

> example of a downstream crate

extern crate demo_hack;
use demo_hack::add_one;

fn main() {
    let two = 2;
    let nine = add_one!(two) + add_one!(2 + 3);
    println!("nine = {}", nine);
}

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this hack by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~1MB
~20K SLoC