macro_rules! token types

Before we build more complex macros, it's important to become familiar with the valid inputs that macro_rules! can take. Since macro_rules! work at the syntactic level, it needs to provide users, a handle to these syntactic elements, and distinguish what can and cannot be included within a macro and how we can interact with them.

The following are some important token tree types that you can pass into a macro as input:

  • block: This is a sequence of statements. We have already used block in the debugging example. It matches any sequence of statements, delimited by braces, such as what we were using before:
{ silly; things; } 

This block includes the statements silly and things.

  • expr: This matches any expression, for example:
    • 1
    • x + 1
    • if x == 4 { 1 } else { 2 }
  • ident: This matches an identifier. Identifiers are any unicode strings that are not keywords (such as if or let). As an exception, the underscore character alone is not an identifier in Rust. Examples of identifiers are as follows:
    • x
    • long_identifier
    • SomeSortOfAStructType
  • item: This matches an item. Module-level things are idenitified as items. These include functions, use declarations, type definitions, and so on. Here are some examples:
    • use std::io;
    • fn main() { println!("hello") }
    • const X: usize = 8;

These do not have to be one-liners, of course. The main function could be a single item, even if it spanned several lines.

  • meta: A meta item. The parameters inside attributes are called meta items, which are captured by meta. The attributes themselves look as follows:
    • #![foo]
    • #[baz]
    • #[foo(bar)]
    • #[foo(bar="baz")]
    • Meta items are the things that are found inside brackets. So, for each of the preceding attributes, the corresponding meta items are as follows:
      • foo
      • baz
      • foo(baz)
      • foo(bar="baz")
  • pat: This is a pattern. Match expressions have patterns on the left-hand side of each match, which pat captures. Here are some examples:
    • 1
    • "x"
    • t
    • *t
    • Some(t)
    • 1 | 2 | 3
    • 1 ... 3
    • _
  • path: It matches a qualified name. Paths are qualified names, that is, names with a namespace attached to them. They're quite similar to identifiers, except that they allow the double colon in their names because they signify paths. Here are some examples:
    • foo
    • foo::bar
    • Foo
    • Foo::Bar::baz

This is useful in cases where you need to capture the path of some type so that you can use it later in code generation, such as when aliasing complex types with paths.

  • stmt: This is a statement. Statements are like expressions, except that more patterns are accepted by stmt. The following are some examples of this:
    • let x = 1
    • 1
    • foo
    • 1+2

In contrast to the first example, let x = 1 wouldn't be accepted by expr.

  • tt: This is a token tree, which is a sequence of other tokens. The tt keyword captures a single token tree. A token tree is either a single token (such as 1, +, or "foo bar") or several tokens surrounded by any of the braces, (), [], or {}. The following are some examples:
    • foo
    • { bar; if x == 2 { 3 } else { 4 }; baz }
    • { bar; fi x == 2 ( 3 ] ulse ) 4 {; baz }

As you can see, the insides of the token tree do not have to make semantic sense; they just have to be a sequence of tokens. Specifically, what does not match this are two or more tokens not enclosed in braces (such as 1 + 2). This is the most general sequence of code or tokens macro_rules! can capture.

  • ty: This is a Rust type. The ty keyword captures things that look like types. Here are some examples:
    • u32
    • u33
    • String

No semantic checking that the type is actually a type is done in the macro expansion phase, so "u33" is accepted just as well as "u32". However, once the code gets generated and goes to the semantic analysis phase, the type is checked, giving an error message of error: expected type, found `u33`. This is used when you are generating code to create a function or implementing methods of a trait on a type.

  • vis: This represents a visibility modifier. This captures visibility modifiers pub, pub(crate), and so on. This is helpful when you are generating module-level code and need to capture privacy modifiers in code fragments that have been passed to the macro.
  • lifetime: Identifies a lifetime such as 'a, 'ctx, 'foo, and so on.
  • literal: A literal that can be any token, like a string literal such as "foo" or an identifier such as bar.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset