6 releases

0.2.1 Jul 19, 2023
0.2.0 Jul 19, 2023
0.1.2 Jul 4, 2020
0.1.1 Jul 4, 2020
0.1.0 Jul 4, 2020

#1526 in Rust patterns

Download history 1529/week @ 2024-09-22 1072/week @ 2024-09-29 1206/week @ 2024-10-06 970/week @ 2024-10-13 1215/week @ 2024-10-20 1311/week @ 2024-10-27 1712/week @ 2024-11-03 1225/week @ 2024-11-10 1608/week @ 2024-11-17 930/week @ 2024-11-24 849/week @ 2024-12-01 487/week @ 2024-12-08 1457/week @ 2024-12-15 189/week @ 2024-12-22 467/week @ 2024-12-29 1518/week @ 2025-01-05

3,655 downloads per month
Used in 7 crates (2 directly)

Zlib OR MIT OR Apache-2.0

11KB

::defile

Helper proc-macro to "ungroup" a captured metavariable (thus potentially breaking their hygiene, hence the name).

Repository Latest version Documentation MSRV unsafe forbidden License CI no_std

This is useful when using helper macro_rules macros, that need to parse using some special rule (e.g. :expr, :path, :pat), but that later want to further inspect the captured variable.

This is not something a macro_rules! can do on its own, since such so-called metavariables are seen as an opaque single token (:tt) (the sequence of tokens captured in the metavariable have been grouped (≈ parenthesized) but using invisible parenthesis.

Example

macro_rules! check_expr {
    (
        42
    ) => ({
        println!("Got `42`!");
    });

    (
        $($tt:tt)*
    ) => ({
        println!("Did not get `42`. Instead, got the following tokens:\n[");
        $(
            println!("    `{}`,", stringify!($tt));
        )*
        println!("]");
    });
}

macro_rules! check_all_exprs {(
    $(
        $expr:expr // use :expr to be able to use `,` as a delimiter
    ),* $(,)?
) => (
    fn main () {
        $(
            println!("vvvvvvvvvvvvv");
            check_expr!($expr);
            println!("^^^^^^^^^^^^^\n");
        )*
    }
)}

check_all_exprs!(42, 1 + 1);

outputs:

vvvvvvvvvvvvv
Did not get `42`. Instead, got the following tokens:
[
    `42`,
]
^^^^^^^^^^^^^

vvvvvvvvvvvvv
Did not get `42`. Instead, got the following tokens:
[
    `1 + 1`,
]
^^^^^^^^^^^^^
  • That is:

    • The token 42 does not match 42!

    • That being said, the expression 1 + 1 is viewed as a single indivisible token too.

      Indeed, that's kind of the point of this behavior: if we do 2 * $expr where $expr captures 1 + 1 we expect the result to be 2 * (1 + 1) instead of 2 * 1 + 1!

But by doing:

macro_rules! check_all_exprs {(
    $(
        $expr:expr // use :expr to be able to use `,` as a delimiter
    ),* $(,)?
) => (::defile::defile! { // 👈
    fn main () {
        $(
            println!("vvvvvvvvvvvvv");
            check_expr!(@$expr);
//                      👆
            println!("^^^^^^^^^^^^^\n");
        )*
    }
})}

we do get:

vvvvvvvvvvvvv
Got `42`!
^^^^^^^^^^^^^

vvvvvvvvvvvvv
Did not get `42`. Instead, got the following tokens:
[
    `1`,
    `+`,
    `1`,
]
^^^^^^^^^^^^^
  • 42 has matched the literal 42, but be aware that this has also resulted in 1 + 1 getting split. So, if you were to defile expressions such as 2 * @$expr, you may not obtain the expected result! Use with caution.

Caveats

Currently (1.45.0), there are several bugs regarding the interaction between macro_rules! macros and procedural macros, that may lead to defile! and any other helper procedural macro to split groups that are not @-prefixed.

Hopefully those bugs are solved, making the actual implementation of defile! meaningful.

Dependencies