Search code examples
syntaxrustrust-macrosrust-decl-macros

What does an @ symbol mean in a Rust declarative macro?


I have seen the @ symbol used in macros but I cannot find mention of it in the Rust Book or in any official documentation or blog posts. For example, in this Stack Overflow answer it is used like this:

macro_rules! instructions {
    (enum $ename:ident {
        $($vname:ident ( $($vty: ty),* )),*
    }) => {
        enum $ename {
            $($vname ( $($vty),* )),*
        }

        impl $ename {
            fn len(&self) -> usize {
                match self {
                    $($ename::$vname(..) => instructions!(@count ($($vty),*))),*
                }
            }
        }
    };

    (@count ()) => (0);
    (@count ($a:ty)) => (1);
    (@count ($a:ty, $b:ty)) => (2);
    (@count ($a:ty, $b:ty, $c:ty)) => (3);
}

instructions! {
    enum Instruction {
        None(),
        One(u8),
        Two(u8, u8),
        Three(u8, u8, u8)
    }
}

fn main() {
    println!("{}", Instruction::None().len());
    println!("{}", Instruction::One(1).len());
    println!("{}", Instruction::Two(1, 2).len());
    println!("{}", Instruction::Three(1, 2, 3).len());
}

From the usage, it appears that it is used for declaring another macro that is local to the main one.

What does this symbol mean and why would you use it rather than just creating another top-level macro?


Solution

  • In the pattern-matching part of a macro, symbols can mean whatever the author desires them to mean. A leading symbol @ is often used to denote an "implementation detail" of the macro — a part of the macro that an external user is not expected to use.

    In this example, I used it to pattern-match the tuple parameters to get a count of the tuple parameters.

    Outside of macros, the @ symbol is used to match a pattern while also assigning a name to the entire pattern:

    match age {
        x @ 0 => println!("0: {}", x),
        y @ 1 => println!("1: {}", y),
        z => println!("{}", z),
    }
    

    With a bit of a stretch, this same logic can be applied to the use in the macro — we are pattern-matching the tuple, but also attaching a name to that specific pattern. I think I've even seen people use something even more parallel: (count @ .... However, The Little Book of Rust Macros points out:

    The reason for using @ is that, as of Rust 1.2, the @ token is not used in prefix position; as such, it cannot conflict with anything. Other symbols or unique prefixes may be used as desired, but use of @ has started to become widespread, so using it may aid readers in understanding your code.


    rather than just creating another top-level macro

    Creating another macro is likely better practice, but only in modern Rust. Before recent changes to Rust that made it so you could import macros directly, having multiple macros could be tricky for end users who tried to selectively import macros.

    See also: