Почему макрогигиена не предотвращает коллизии между несколькими определениями констант?

Я думал, что «гигиена» предотвратит коллизии между X, определенными в моем макросе m!, но это оказалось не так. Что я неправильно понимаю?

macro_rules! m {
    ($e:expr) => {
        const X: i32 = $e;
    };
}

m!(0);
m!(1);

fn main() {
    m!(2);
    m!(3);
}

Игровая площадка

Сообщение об ошибке:

error[E0428]: the name `X` is defined multiple times
 --> src/main.rs:3:9
  |
3 |         const X: i32 = $e;
  |         ^^^^^^^^^^^^^^^^^^
  |         |
  |         `X` redefined here
  |         previous definition of the value `X` here
...
7 | m!(0);
  | ------ in this macro invocation
  |
  = note: `X` must be defined only once in the value namespace of this module

person nodakai    schedule 26.03.2016    source источник


Ответы (1)


Из Языка программирования Rust (первое издание) раздел макрогигиены:

Это [т.е. переименование] действует для let привязок и меток циклов, но не для элементов

Справочник по Rust определяет элементы:

Элемент — это компонент ящика. Элементы организованы в ящике с помощью вложенного набора модулей. Каждый крейт имеет один «самый внешний» анонимный модуль; все последующие элементы в крейте имеют пути в дереве модулей крейта.

Элементы полностью определяются во время компиляции, обычно остаются фиксированными во время выполнения и могут находиться в постоянной памяти.

Есть несколько видов предметов:

  • модули
  • extern crate декларации
  • use декларации
  • определения функций
  • определения типов
  • определения структур
  • определения перечисления
  • определения союзов
  • постоянные элементы
  • статические элементы
  • определения черт
  • реализации
  • extern блоков

Это имеет смысл: если вы вводите элемент в макрос, вы, вероятно, хотите фактически использовать его из других элементов/модулей/ящиков (и, следовательно, вне макроса), но вы не можете, если не знаете его имя, поэтому компилятор не может его переименовать.

person Alexey Romanov    schedule 27.03.2016
comment
Спасибо. Так это по дизайну. если вы вводите элемент в макрос, вы, вероятно, хотите его использовать, ну, я думаю, это не то, как мы должны использовать то, что рекламируется как гигиеническая макросистема... - person nodakai; 27.03.2016
comment
@nodakai Извините, я пропустил там макрос. В вашем случае, если вы поместите println!("X = {}", X); после последнего m!(3), что вы ожидаете от него напечатать? - person Alexey Romanov; 27.03.2016
comment
Я ожидал ошибку неизвестной переменной X, когда слышал, что макросы Rust гигиеничны. - person nodakai; 27.03.2016
comment
И это это то, что вы получите для переменной (let привязка), но const не вводит ее. - person Alexey Romanov; 27.03.2016