Как использовать макрос в файлах модуля?

92

У меня есть два модуля в отдельных файлах в одном ящике, в котором ящик был macro_rulesвключен. Я хочу использовать макросы, определенные в одном модуле, в другом модуле.

// macros.rs
#[macro_export] // or not? is ineffectual for this, afaik
macro_rules! my_macro(...)

// something.rs
use macros;
// use macros::my_macro; <-- unresolved import (for obvious reasons)
my_macro!() // <-- how?

В настоящее время я обнаружил ошибку компилятора " macro undefined: 'my_macro'" ... что имеет смысл; макросистема запускается перед модульной системой. Как мне обойти это?

пользователь
источник
Не надо; ты не используешьmodule::my_macro!()?
u_mulder
2
nope (not afaik) - сообщается, что префикс модуля игнорируется (согласно сообщению компилятора).
пользователь

Ответы:

131

Макросы в одном ящике

#[macro_use]
mod foo {
    macro_rules! bar {
        () => ()
    }
}

bar!();    // works

Если вы хотите использовать макрос в том же ящике, модуль, в котором определен ваш макрос, нуждается в атрибуте #[macro_use].

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

bar!();  // ERROR: cannot find macro `bar!` in this scope

#[macro_use]
mod foo {
    macro_rules! bar {
        () => ()
    }
}

Макросы в ящиках

Чтобы использовать macro_rules!макрос из других ящиков, самому макросу нужен атрибут #[macro_export]. Импортирующий ящик может затем импортировать макрос через use crate_name::macro_name;.

Ящик util

#[macro_export]
macro_rules! foo {
    () => ()
}

Ящик user

use util::foo;

foo!();

Обратите внимание, что макросы всегда находятся на верхнем уровне ящика; так что даже если fooбы был внутри mod bar {}, userящик все равно пришлось бы писать, use util::foo;а не use util::bar::foo; .

До Rust 2018 вам приходилось импортировать макрос из других ящиков, добавляя атрибут #[macro_use]в extern crate util;оператор. Это импортирует все макросы из util. В качестве альтернативы #[macro_use(cat, dog)]можно использовать только для импорта макросов catи dog. В этом синтаксисе больше нет необходимости.

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

Лукас Калбертодт
источник
27
«Макросы можно использовать только после того, как они были определены». - Это ключ, потому что вы можете столкнуться с этой ошибкой, даже если вы сделали все остальное правильно. Например, если у вас есть модули macrosи foo(который использует макрос из macros), и вы перечисляете их в алфавитном порядке в ваших lib.rs или main.rs, foo будет загружен перед макросами, и код не будет компилироваться.
neverfox
7
^ совет от профи - это меня полностью достало
semore_1267
3
Также обратите внимание, что для внутреннего использования макросов #[macro_use]атрибут должен быть в каждом модуле, родительском модуле и т. Д. До тех пор, пока он не достигнет точки, в которой вам нужно его использовать.
Десять
Этот ответ не сработал для меня. Модуль, в котором был объявлен макрос, #[macro_use]был объявлен первым в lib.rs - по-прежнему не работал. Ответ @ Ten помог, и я добавил #[macro_use]в начало lib.rs - тогда это сработало. Но я все еще не уверен, что лучше всего, так как я прочитал здесь, что «Вы не импортируете макросы из других модулей; вы экспортируете макрос из определяющего модуля»
Сорин Болос,
Я всегда забываю, как макросы Rust работают с модулями. Это ужасная система, и, надеюсь, когда-нибудь появится лучшая.
Хатч Мур,
20

Этот ответ устарел в версии Rust 1.1.0-stable.


Вам нужно добавить #![macro_escape]вверху macros.rsи включить его, mod macros;как указано в Руководстве по макросам .

$ cat macros.rs
#![macro_escape]

#[macro_export]
macro_rules! my_macro {
    () => { println!("hi"); }
}

$ cat something.rs
#![feature(macro_rules)]
mod macros;

fn main() {
    my_macro!();
}

$ rustc something.rs
$ ./something
hi

Для дальнейшего использования,

$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)
Догберт
источник
Я совершенно упустил этот атрибут. Благодарность!
пользователь
4
Кстати, #[macro_export]атрибут здесь не нужен. Это необходимо только в том случае, если макрос должен быть экспортирован для внешних пользователей ящика. Если макрос используется только внутри ящика, #[macro_export]не нужен.
Владимир Матвеев
1
Большое спасибо за ответ. Я просто хочу добавить, что если в вашем something.rsфайле используются другие модули, например с mod foobar;, и этот foobarмодуль использует макросы из macro.rs, то вы должны поставить mod macro; раньше, mod foobar; чтобы программа скомпилировалась. Мелочь, но это не очевидная ИМО.
conradkleinespel
2
(nb, этот ответ теперь устарел; я принял последний ответ, данный Лукасом)
пользователь
7

Добавление #![macro_use]макросов в начало файла приведет к тому, что все макросы будут перенесены в main.rs.

Например, предположим, что этот файл называется node.rs:

#![macro_use]

macro_rules! test {
    () => { println!("Nuts"); }
}

macro_rules! best {
    () => { println!("Run"); }
}

pub fn fun_times() {
    println!("Is it really?");
}

Ваш main.rs когда-нибудь будет выглядеть следующим образом:

mod node;  //We're using node.rs
mod toad;  //Also using toad.rs

fn main() {
    test!();
    best!();
    toad::a_thing();
}

Наконец, допустим, у вас есть файл с именем toad.rs, который также требует этих макросов:

use node; //Notice this is 'use' not 'mod'

pub fn a_thing() {
  test!();

  node::fun_times();
}

Обратите внимание, что как только файлы помещаются в main.rs с помощью mod, остальные ваши файлы получают к ним доступ через useключевое слово.

Люк Дюпен
источник
Я добавил дополнительные пояснения. Начиная с rustc 1.22.1, это работает.
Люк Дюпен
Ты уверен? Где задокументировано это #! [Macro_use] (а не # [macro_use])? Я не могу его найти. Здесь не работает.
Маркус
Это сработало, когда я опубликовал это, система включения Rust - это ужасный беспорядок, вполне возможно, что это больше не работает.
Люк Дюпен
@Markus Обратите внимание, что #![macro_use]оператор находится ВНУТРИ макромодуля, а не снаружи. В #![...]синтаксическом соответствует имеющим атрибутам применяются к их содержащим областям, например , #![feature(...)](очевидно , это не имело бы смысл , если записать в виде #[feature(...)], было бы семантический требуют, чтобы компилятор включить определенные функции по конкретным вопросам в клети, а не вся корневой обрешетке). Итак, как сказал @LukeDupin, модульная система - беспорядок, хотя, возможно, по другой причине, чем на первый взгляд.
пользователь
Я действительно хочу, чтобы в этом ответе упоминалось, что конструкция не совсем идиоматична (в стороне, мне нравится ответ). Несмотря на свою (не) -идиоматичность, это интересно, потому что размещение его рядом с идиоматической формой делает до боли очевидным, что макросы взаимодействуют с модульной системой иначе, чем обычные конструкции. Или, по крайней мере, издает сильный запах (как только что продемонстрировал @Markus, который с этим справился).
пользователь
2

Я столкнулся с той же проблемой в Rust 1.44.1, и это решение работает для более поздних версий (известно, что оно работает в Rust 1.7).

Допустим, у вас есть новый проект:

src/
    main.rs
    memory.rs
    chunk.rs

В main.rs вам нужно отметить , что вы импортируете макросы из источника, иначе это вам не поможет.

#[macro_use]
mod memory;
mod chunk;

fn main() {
    println!("Hello, world!");
}

Итак, в memory.rs вы можете определять макросы, и вам не нужны аннотации:

macro_rules! grow_capacity {
    ( $x:expr ) => {
        {
            if $x < 8 { 8 } else { $x * 2 }
        }
    };
}

Наконец, вы можете использовать его в chunk.rs , и вам не нужно включать макрос сюда, потому что это делается в main.rs:

grow_capacity!(8);

Upvoted ответ вызвал замешательство у меня с этим документом на примере , было бы полезно тоже.

knh190
источник
Принятый ответ буквально имеет , что в первых строках первого блока кода: #[macro_use] mod foo {.
Shepmaster
1
@Shepmaster, в ответе, за который проголосовали, есть определение макросов и оператор импорта в одном и том же месте, поэтому это вызвало путаницу (для меня). Я использовал #[macro_use]определение. Компилятор не говорит, что это неуместно.
knh190
При желании вы можете перечитать doc.rust-lang.org/book/... тогда.
Shepmaster
Спасибо за такой ответ! Меня тоже смутил принятый ответ, и я не мог понять его, пока не прочитал ваше объяснение.
Prgrm.celeritas
@Shepmaster В разделе, на который вы ссылаетесь, не упоминается, как работают макросы. Вы хотели дать ссылку на другую часть книги?
подробно