Можно ли использовать макросы Rust в разных редакциях?
Скажем, макрос Rust 2018 определяет
Я не так хорошо знаком с внутренней работой процедурных или декларативных макросов, но я полагаю, что они должны создавать код для конкретной редакции, потому что их вывод будет обрабатываться так же, как и остальной код. Если да, то как я могу поделиться экспортом макросов через границы редакции. Нужно ли мне переписывать их для каждой редакции? Это не кажется масштабируемым, особенно если выпуски предполагается выпускать каждые 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
не будет работать правильно.