Как rust принимает решение о том, какую ветку макросов использовать?

#rust #rust-macros

#Ржавчина #rust-макросы

Вопрос:

Рассмотрим следующий фрагмент кода

 #[macro_export]
macro_rules! test {
    ($a:literal) => { $a };
    ($a:expr) => { $a };
}

fn func() {
    let a = test!(0);
    let b = test!(a);
    let c = test!(-a);
}
 

Почему третье задание не работает? По какой-то причине из-за - (я думаю) сбоя в первой макро-ветви. Я бы ожидал, что он будет использовать второе. Почему это не так?

Я получаю следующее сообщение об ошибке:

 error: unexpected token: `a`
   --> src/gate.rs:144:20
    |
137 |     ($a:literal) => { $a };
    |      ---------- while parsing argument for this `literal` macro fragment
...
144 |     let c = test!(-a);
    |                    ^
 

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

 warning: unnecessary parentheses around assigned value
   --> src/gate.rs:144:19
    |
144 |     let c = test!((-a));
    |                   ^^^^ help: remove these parentheses
    |
    = note: `#[warn(unused_parens)]` on by default
 

Комментарии:

1. Это всего лишь догадка: анализатор входит в literal ветку, потому что символ «-» совпадает как токен для литерала (например, «-5»); затем он попадает в ловушку, потому что «a» не может быть частью любого литерала, начинающегося с «-«; он не оценивает другие ветвиперед ошибкой. Круглые скобки заставляют expression брать ветку, потому что круглые скобки не являются частью какого-либо литерала.

2. Это действительно странно, для меня это похоже на ошибку. Я мог бы предложить открыть проблему в репозитории GitHub.

3. Глядя на документы, это действительно похоже на ошибку, поскольку «Обратите внимание, что синтаксис Rust рассматривает -1i8 как применение унарного оператора минус к целочисленному литералу 1i8, а не к одному целочисленному литералу». Я открою проблему

4. См. Проблему