Что означает метастабильный тип tt в макросах Rust?

Я читаю книгу о Rust и начинаю играть с Rust macros. Здесь описываются все метапарабельные типы, и есть примеры, кроме последнего - tt. Согласно книге, это "единственное дерево-маркер". Мне любопытно, что это такое и для чего он используется? Можете ли вы привести пример?

Ответ 1

Это понятие введено, чтобы гарантировать, что все, что находится в вызове макроса, правильно соответствует парам (), [] и {}. tt сопоставляет любой токен или любую пару скобок/скобок/скобок с их содержанием.

Например, для следующей программы:

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

Токен-деревья будут:

  • fn
  • main
  • ()
  • { println!("Hello world!"); }
    • println
    • !
    • ("Hello world!")
      • "Hello world!"
    • ;

Каждый из них образует дерево, в котором простые токены (fn, main и т.д.) Являются листьями, а все, что окружено (), [] или {}, имеет поддерево. Обратите внимание, что ( не отображается отдельно в дереве токенов: невозможно сопоставить ( без сопоставления с соответствующим ).

Например:

macro_rules! {
    (fn $name:ident $params:tt $body:tt) => { /* … */ }
}

будет соответствовать вышеуказанной функции с $name → main, $params → (), $body → { println!("Hello world!"); }.

Дерево токенов - это наименее требовательный тип метавариабельной переменной: он соответствует чему угодно. Он часто используется в макросах, в которых есть "не очень важно", особенно в макросах, в которых есть "голова" и "хвост". Например, макросы println! имеют ветвь, совпадающую с ($fmt:expr, $($arg:tt)*), где $fmt - строка формата, а $($arg:tt)* означает "все остальное" и просто пересылается в format_args!. Это означает, что println! не нужно знать фактический формат и выполнять сложное сопоставление с ним.