2

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?

Greg Owen
  • 947
  • 8
  • 19

1 Answers1

4

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.

Optimistic Peach
  • 3,862
  • 2
  • 18
  • 29
  • Ah, so the `expected item` error is being thrown because I have two sets of curly braces in the post-expansion code, not because I invoked the macro on something that should have been an `item` but wasn't. Thanks! – Greg Owen Mar 29 '20 at 00:58
  • Yep! An easy way of telling this is to see where the error is produced. If the token came from the input of the macro, and your resulting code is erroneous, the error still points to the input token, not the macro's implementation. – Optimistic Peach Mar 29 '20 at 01:11