Можно ли использовать макросы Rust в разных редакциях?

Скажем, макрос Rust 2018 определяет функции внутри него. Синтаксис, который он будет использовать, будет несовместим с Rust 2015. Итак, если вы компилируете свой ящик с версией 2015 года, разве этот расширенный код из макроса не будет конфликтовать с этим?

Я не так хорошо знаком с внутренней работой процедурных или декларативных макросов, но я полагаю, что они должны создавать код для конкретной редакции, потому что их вывод будет обрабатываться так же, как и остальной код. Если да, то как я могу поделиться экспортом макросов через границы редакции. Нужно ли мне переписывать их для каждой редакции? Это не кажется масштабируемым, особенно если выпуски предполагается выпускать каждые 3 года или около того.

2 ответа

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

Это было тщательно спланировано как часть механизма Edition, чтобы предотвратить стагнацию экосистемы. В частности, для макросов руководство Rust Edition объясняет это более подробно:

Макросы используют систему, называемую «гигиена выпуска», где токены внутри макроса помечаются тем, из какого выпуска они получены. Это позволяет вызывать внешние макросы из ящиков различных выпусков, не беспокоясь о том, из какого выпуска они вызываются.

Приведен пример макроса, который работает только в версии 2015:

      #[macro_export]
macro_rules! foo {
    () => {
        let dyn = 1;
        println!("it is {}", dyn);
    };
}

Это использует dynв качестве идентификатора, что недопустимо в Rust 2018. Однако, поскольку этот макрос был написан в версии 2015, любой код, написанный в этом контексте, анализируется и интерпретируется в соответствии с правилами этой редакции, изолированно от кода вызывающей стороны. Благодаря такой «гигиене» макросов их можно без проблем использовать в Rust 2018, 2021 или любой другой редакции.

То же самое относится и к макросам, написанным в более поздних редакциях . Даже если процедурный макрос объявляет asyncфункция, в конечном итоге есть общая основа, которая не зависит от этого синтаксиса (MIR).

Единственным исключением из этого, конечно же, является миграция существующего кода между редакциями .

Одно из правил редакций ржавчины заключается в том, что ящики должны быть совместимы, даже если они не из одной редакции. (источник)

Это правило распространяется и на макросы, благодаря свойству гигиены редактирования. Каждый диапазон макросов помечен своим выпуском (исходным кодом), и когда макрос расширяется, шаг синтаксического анализа выполняется в его выпуске объявления, а не в месте вызова выпуска.

На нижнем уровне представления разницы между версиями ржавчины больше не существует, поэтому все компилируется и запускается (исходник)

Тем не менее, проблема миграции с макросами может возникнуть, когда макрос определен и не используется в переносимом крейте. В таком случае, cargo fix --editionне будет работать правильно.

Другие вопросы по тегам