Rust supports function-like proc macros in expression position!? 🎉

Continuing the discussion from How to make rust usable in schools:

Totally random, but dude, thanks for that link! I was just reading through it and found the section about proc_macros in expression position. That's awesome and something I've seen hold multiple projects on nightly. I didn't know that was out yet.

This is likely to helpe me out in my own projects.

1 Like

I keep forgetting, can proc macros expand to macro_rules macro definitions now? I think I read that somewhere and have wished for that feature in the past, but can't quickly find it atm.

Yes, they can now. This was stabilized in 1.40.0 [2019-12-19] (release notes).

3 Likes

Nit: that wasn't the case for a while, thanks to

I think the existence of that hack is precisely what motivated that stabilization, btw.


For those curious: how does ::proc-macro-hack work

The idea is that the moment some procedural macro, be it attribute, derive, etc., is able to emit a macro_rules! call, you can then do:

fn the_proc_macro ... { TokenStream::from(quote! {
    macro_rules! emit {() => (
        /* the actual expansion here */
    )}
})}

and then have the frontend crate do:

#[macro_export]
macro_rules! macro_in_expr_position {(
    $($input:tt)*
) => ({
    // this defines a `macro_rules! emit {() => (output)}` definition.
    $crate::proc_macro_backend::the_proc_macro! { $($input)* }
    emit!()
})}

And it turns out #[proc_macro_derive]s had that ability to emit macro_rules! definitions very early on. This means that any proc-macro could emit a dummy:

#[derive(Helper)]
enum Dummy { Hack = (stringify!( <proc-macro-output> ), 0).1 }

which is a valid item for the proc-macro to emit, then the Helper derive could fetch the <proc-macro-output> and define the macro_rules! emit {() => (<proc-macro-output>)} and then the frontend macro_rules! shim could append the emit!() call in expression position.

4 Likes

Yeah, I did know about that and was very glad that the hack existed I just hadn't tried it yet. I was actually planning to eventually try to bring the reql crate off of nightly using that, but never got to it.

It's just nice that now you don't need to have an extra crate and do a little juggle to get it to work.

1 Like