What does an @ symbol mean in a declarative macro?

2020-02-07 02:08发布

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?

1条回答
时光不老,我们不散
2楼-- · 2020-02-07 03:01

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:

查看更多
登录 后发表回答