13

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?

JGFMK
  • 8,425
  • 4
  • 58
  • 92
Peter Hall
  • 53,120
  • 14
  • 139
  • 204

1 Answers1

15

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:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Ah... so obvious! I somehow thought it was special syntax and couldn't shake the idea. – Peter Hall Jan 28 '19 at 16:50
  • Wait so `x @ 0` is equivalent to `x if x == 0`? What's the point of introducing another symbol? – Optimistic Peach Jan 28 '19 at 22:43
  • @OptimisticPeach it's not a common use case, certainly, but there are cases where you need to pattern match and bind a name. [A quickly-created example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=65561d5922679e7f0bc4c560eedde24f). You could make the same argument for match guards as well, as many times they can be `if` expressions in the match arm instead. – Shepmaster Jan 29 '19 at 00:27
  • `$($ename::$vname(..) => instructions!(@count ($($vty),*))),*` – I didn't get this part. How does the number of arguments inside `$vname(..)` equate to the number of `$vty` on the right side. – vikram2784 Jan 30 '19 at 10:57