Як використовувати макрос у файлах модулів?


93

У мене є два модулі в окремих файлах в одному ящику, де ящик 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'" ... що має сенс; макросистема запускається перед модульною системою. Як я можу обійти це?


Shouldn; 't you usemodule::my_macro!()?
u_mulder

2
nope (не 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це буде всередині a mod bar {}, userящик все одно повинен писати, use util::foo;а ні use util::bar::foo; .

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

Детальніша інформація доступна у розділі Мова програмування іржі про макроси .


27
"Макроси можна використовувати лише після їх визначення". - Це ключово, тому що ви можете зіткнутися з цією помилкою, навіть коли зробили всі інші речі, згадані правильно. Наприклад, якщо у вас є модулі macrosі foo(який використовує макрос від macros), і ви перелічуєте їх в алфавітному порядку у вашому lib.rs або main.rs, foo буде завантажено перед макрокомандами, і код не буде компілюватися.
neverfox

7
^ pro tip - це мене абсолютно зрозуміло
semore_1267

3
Також зауважте, що для внутрішнього використання макросів #[macro_use]атрибут повинен знаходитись на кожному модулі, батьківському модулі тощо, поки він не досягне точки, де вам потрібно його використовувати.
10

Ця відповідь для мене не спрацювала. Модуль, який оголосив макрос #[macro_use]і був оголошений першим у lib.rs - все ще не працював. Відповідь Тена допомогла, і я додав #[macro_use]у верхню частину lib.rs - тоді це спрацювало. Але я все ще не впевнений, яка найкраща практика, оскільки я тут прочитав, що "Ви не імпортуєте макроси з інших модулів; ви експортуєте макрос із визначального модуля"
Сорін Болос

Я завжди забуваю, як макроси Руста працюють з модулями. Це жахлива система, і, сподіваємось, колись буде краща.
Хатч Мур,

20

Ця відповідь застаріла станом на Rust 1.1.0-стабільна.


Вам потрібно додати #![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; компіляцією програми. Незначна річ, але це не очевидне IMO.
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 with mod, інші ваші файли отримують до них доступ за допомогою useключового слова.


Я додав більше роз’яснень. З rustc 1.22.1 це працює.
Luke Dupin

Ти впевнений? Де це #! [Макро_користування] (а не # [макро_користування]) задокументовано? Не можу знайти. Тут це не працює.
Маркус

Це спрацювало, коли я його розмістив, система включення Руста - це такий жахливий безлад, цілком можливо, це вже не працює.
Люк Дупін,

@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 відповідь викликав замішання у мене з цим документом на прикладі , було б корисно теж.


Прийнятий відповідь буквально має , що в перших рядках першого блоку коду: #[macro_use] mod foo {.
Шепмастер

1
@Shepmaster, прихильна відповідь має визначення макросів та оператора імпорту в одному місці, тому це спричинило плутанину (для мене). Я використовував #[macro_use]у визначенні. Компілятор не каже, що це неправильно.
knh190

Тоді ви можете перечитати doc.rust-lang.org/book/… .
Шепмастер

Дякую за цю відповідь! Я також був розгублений прийнятою відповіддю і не міг зрозуміти, поки не прочитав ваше пояснення.
Prgrm.celeritas

@Shepmaster У розділі, на який ви посилаєтесь, немає згадки про те, як працюють макроси. Ви мали на увазі посилання на якусь іншу частину книги?
померло
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.