Search code examples
rustrust-macros

Understanding `tt` in Rust's `macro_rules!` macros


I'm having trouble understanding what exactly a tt is in Rust's macro_rules! macros.

From this answer, I thought that

tt will match any single token or any pair of parenthesis/brackets/braces with their content.

However, the following example does not seem to follow this rule:

macro_rules! foo {
  (content: $content:tt) => {
    mod foo {
      $content
    }
  }
}

foo! (
  content: {
    pub fn main() {
      println!("Hello");
    }  
  }
);

I would expect that the tt would match everything contained in the {} after content: and that the result of the macro invocation would be

mod foo {
  pub fn main() {
    println!("Hello");
  }
}

Instead, I get the following error message:

error: expected item, found `{`
  --> src/main.rs:10:12
   |
10 |   content: {
   |            ^ expected item

What's going wrong here? Also, why does Rust tell me it's expecting an item when I've told it to expect a tt?


Solution

  • tt is working as expected here. Your macro invocation is as follows:

    foo! (
      content: {
        pub fn main() {
          println!("Hello");
        }  
      }
    );
    

    $content is this:

    {
        pub fn main() {
            println!("Hello");
        }  
    }
    

    So, the result is this:

    mod foo {
        { // <-- Error occurs here
            pub fn main() {
                println!("Hello");
            }  
        }
    }
    

    You cannot have another set of curly braces directly inside of a mod declaration.

    The solution to make your code work is to just put $content directly after mod foo, but I presume you already saw that:

    macro_rules! foo {
      (content: $content:tt) => {
        mod foo $content
      }
    }
    
    foo! (
      content: {
        pub fn main() {
          println!("Hello");
        }  
      }
    );
    

    Playground.