Почему я не могу передать захваченный токен во вложенный макрос? - PullRequest
0 голосов
/ 23 ноября 2018

Множество примеров, которые я видел, предполагают, что это должно быть возможно, но это, очевидно, не так:

lib.rs:

#![feature(trace_macros)]

#[macro_export]
macro_rules! inner_macro (
    (f32) => {"float"};
);

#[macro_export]
macro_rules! outer_macro {
    ($T:ty) => {
        inner_macro!($T)
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn test_nested() {
        trace_macros!(true);
        s1: String = String::from(outer_macro!(f32));
        s2: String = String::from(inner_macro!(f32));
        trace_macros!(false);
    }
}

Запуск cargo test дает следующий вывод:

error: no rules expected the token `f32`
  --> src/lib.rs:11:22
   |
4  | / macro_rules! inner_macro (
5  | |     (f32) => {"float"};
6  | | );
   | |__- when calling this macro
...
11 |           inner_macro!($T)
   |                        ^^ no rules expected the token `f32`
...
21 |           s1: String = String::from(outer_macro!(f32));
   |                                     ----------------- in this macro invocation

Это сбивает с толку, потому что, безусловно, существует правило, ожидающее токен f32.

Есть также примечания из трассировки расширения двух макросов.Первый не работает:

= note: expanding `outer_macro! { f32 }`
= note: to `inner_macro ! ( f32 )`
= note: expanding `inner_macro! { f32 }`

, а второй работает:

= note: expanding `inner_macro! { f32 }`
= note: to `"float"`

Почему первое расширение inner_macro! терпит неудачу, в то время как точно такое же расширениеуспешно, если он не вложен в другой макрос?

Редактировать: если мы выполняем замену вручную, она работает и дает ожидаемый результат:

macro_rules! unknown {
    ($T:ty) => {
        inner_macro!(f32)
    }
}

1 Ответ

0 голосов
/ 23 ноября 2018

После еще чтения оказывается, что это типичный камень преткновения.После первого захвата $T принимает значение AST Node .Подстановка $T не будет заменять токен, она будет заменять этот узел AST.Так что я ожидал, что-то вроде этого:

inner_macro!(`f32` [token])

было на самом деле

inner_macro!(<Type>f32</Type>)

К сожалению для пользователей, оба вызова оба преобразуются в одинаковый способ, до inner_macro! ( f32 ).

Правильный способ сделать это - перехватить токен, который будет заменен, как «дерево токенов»:

macro_rules! unknown {
    ($T:tt) => {
        inner_macro!($T)
    }
}
...