Wie verwende ich ein Makro für alle Moduldateien?

91

Ich habe zwei Module in separaten Dateien in derselben Kiste, in der die Kiste macro_rulesaktiviert wurde. Ich möchte die in einem Modul definierten Makros in einem anderen Modul verwenden.

// 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?

Ich habe momentan den Compilerfehler " macro undefined: 'my_macro'" ... getroffen, was Sinn macht; Das Makrosystem läuft vor dem Modulsystem. Wie kann ich das umgehen?

Nutzer
quelle
Sollte nicht verwendet werdenmodule::my_macro!()?
u_mulder
2
nope (nicht afaik) - das Modulpräfix wird Berichten zufolge ignoriert (gemäß der Compilermeldung).
Benutzer

Antworten:

130

Makros in derselben Kiste

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

bar!();    // works

Wenn Sie das Makro in derselben Kiste verwenden möchten, benötigt das Modul, in dem Ihr Makro definiert ist, das Attribut #[macro_use].

Makros können nur verwendet werden, nachdem sie definiert wurden. Dies bedeutet, dass dies nicht funktioniert:

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

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

Makros über Kisten

Um Ihr macro_rules!Makro aus anderen Kisten zu verwenden, benötigt das Makro selbst das Attribut #[macro_export]. Die importierende Kiste kann dann das Makro über importieren use crate_name::macro_name;.

Kiste util

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

Kiste user

use util::foo;

foo!();

Beachten Sie, dass Makros immer auf der obersten Ebene einer Kiste leben. Selbst wenn fooes sich in einem befinden würde mod bar {}, usermüsste die Kiste immer noch schreiben use util::foo;und nicht use util::bar::foo; .

Vor Rust 2018 mussten Sie Makros aus anderen Kisten importieren, indem Sie das Attribut #[macro_use]zur extern crate util;Anweisung hinzufügen . Das würde alle Makros von importieren util. Alternativ können #[macro_use(cat, dog)]nur die Makros catund importiert werden dog. Diese Syntax sollte nicht mehr erforderlich sein.

Weitere Informationen finden Sie im Kapitel Die Programmiersprache Rust zu Makros .

Lukas Kalbertodt
quelle
27
"Makros können nur verwendet werden, nachdem sie definiert wurden." - Dies ist der Schlüssel, da dieser Fehler auch dann auftreten kann, wenn Sie alle anderen genannten Schritte korrekt ausgeführt haben. Wenn Sie beispielsweise Module macrosund foo(die ein Makro von verwenden macros) haben und diese in alphabetischer Reihenfolge in Ihrer lib.rs oder main.rs auflisten, wird foo vor den Makros geladen und der Code wird nicht kompiliert.
Neverfox
7
^ Pro Tipp - das hat mich total erwischt
semore_1267
3
Beachten Sie auch, dass sich das #[macro_use]Attribut für die interne Verwendung von Makros auf jedem Modul und übergeordneten Modul usw. befinden sollte, bis es den Punkt erreicht, an dem Sie es verwenden müssen.
Zehn
sehr hilfreich, danke
zhaozhi
Diese Antwort hat bei mir nicht funktioniert. Das Modul, das das Makro deklarierte, hatte #[macro_use]und es wurde zuerst in lib.rs deklariert - funktionierte immer noch nicht. @ #[macro_use]Tens Antwort hat geholfen und ich habe oben in lib.rs hinzugefügt - dann hat es funktioniert. Aber ich bin mir immer noch nicht sicher, was die beste Vorgehensweise ist, da ich hier gelesen habe : "Sie importieren keine Makros aus anderen Modulen; Sie exportieren das Makro aus dem definierenden Modul"
Sorin Bolos
20

Diese Antwort ist ab Rust 1.1.0-stabil veraltet.


Sie müssen hinzufügen #![macro_escape]an der Spitze macros.rsund schließen sie mit , mod macros;wie erwähnt in dem Macros Handbuch .

$ 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

Zum späteren Nachschlagen,

$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)
Dogbert
quelle
Ich hatte dieses Attribut total vermisst. Vielen Dank!
Benutzer
4
Übrigens ist ein #[macro_export]Attribut hier nicht erforderlich. Es wird nur benötigt, wenn das Makro an externe Kistenbenutzer exportiert werden soll. Wenn das Makro nur in der Kiste verwendet wird, #[macro_export]wird es nicht benötigt.
Vladimir Matveev
1
Vielen Dank für die Antwort. Ich möchte nur hinzufügen, dass , wenn Ihre something.rsDatei andere Module verwendet, zum Beispiel mit mod foobar;, und dieses foobarModul verwendet den Makros aus macro.rs, dann müssen Sie setzen , mod macro; bevor mod foobar; für das Programm zu kompilieren. Kleinigkeit, aber das ist keine offensichtliche IMO.
Conradkleinespel
2
(nb diese Antwort ist jetzt veraltet; ich habe die aktuelle Antwort von Lukas akzeptiert)
Benutzer
7

Wenn Sie #![macro_use]am Anfang Ihrer Datei Makros hinzufügen, werden alle Makros in main.rs gezogen.

Angenommen, diese Datei heißt node.rs:

#![macro_use]

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

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

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

Ihre main.rs würden irgendwann wie folgt aussehen:

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

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

Nehmen wir zum Schluss an, Sie haben eine Datei namens toad.rs, für die auch diese Makros erforderlich sind:

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

pub fn a_thing() {
  test!();

  node::fun_times();
}

Beachten Sie, dass modder Rest Ihrer Dateien über das useSchlüsselwort Zugriff darauf hat , sobald Dateien in main.rs mit gezogen wurden .

Luke Dupin
quelle
Ich habe mehr Klarstellung hinzugefügt. Ab rustc 1.22.1 funktioniert dies.
Luke Dupin
Bist du sicher? Wo ist diese #! [Makro_use] (nicht # [macro_use]) dokumentiert? Ich kann es nicht finden. Hier funktioniert es nicht.
Markus
Dies hat funktioniert, als ich es gepostet habe. Rusts Include-System ist so ein schreckliches Durcheinander, dass es durchaus möglich ist, dass dies nicht mehr funktioniert.
Luke Dupin
@Markus Beachten Sie, dass die #![macro_use]Aussage INSIDE das Makro-Modul, nicht außerhalb. Die #![...]Syntax entspricht der Anwendung von Attributen auf die enthaltenen Bereiche, z. B. #![feature(...)](dies wäre natürlich nicht sinnvoll, wenn es als geschrieben geschrieben #[feature(...)]würde; es würde semantisch erfordern, dass der Compiler bestimmte Funktionen für bestimmte Elemente in einer Kiste aktiviert und nicht für die gesamte Stammkiste). Wie @LukeDupin sagte, ist das Modulsystem ein Chaos, wenn auch vielleicht aus einem anderen Grund als auf den ersten Blick.
Benutzer
Ich wünschte, diese Antwort würde erwähnen, dass die Konstruktion nicht gerade idiomatisch ist (abgesehen davon gefällt mir die Antwort). Trotz seiner (Nicht-) Diomatizität ist es interessant, weil die Platzierung neben der idiomatischen Form schmerzlich deutlich macht, dass Makros auf andere Weise als die üblichen Konstruktionen mit dem Modulsystem interagieren. Oder es gibt zumindest einen starken Geruch ab (wie gerade gezeigt, dass @Markus sich damit auseinandersetzt).
Benutzer
2

Ich bin in Rust 1.44.1 auf dasselbe Problem gestoßen, und diese Lösung funktioniert für spätere Versionen (bekannt für Rust 1.7).

Angenommen, Sie haben ein neues Projekt als:

src/
    main.rs
    memory.rs
    chunk.rs

In main.rs müssen Sie mit Anmerkungen versehen, dass Sie Makros aus der Quelle importieren. Andernfalls ist dies für Sie nicht ausreichend .

#[macro_use]
mod memory;
mod chunk;

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

In memory.rs können Sie also die Makros definieren und benötigen keine Anmerkungen:

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

Schließlich können Sie es in chunk.rs verwenden , und Sie müssen das Makro hier nicht einfügen , da es in main.rs ausgeführt wird:

grow_capacity!(8);

Die positiv bewertete Antwort hat mich verwirrt, mit diesem Dokument als Beispiel wäre es auch hilfreich.

knh190
quelle
Die akzeptierte Antwort hat buchstäblich das als die ersten Zeilen des ersten Codeblocks : #[macro_use] mod foo {.
Shepmaster
1
@ Shepmaster Die hochgestimmte Antwort enthält die Definition von Makros und die Importanweisung an derselben Stelle, was (für mich) Verwirrung stiftete. Ich habe #[macro_use]in der Definition verwendet. Der Compiler sagt nicht, dass er verlegt ist.
knh190
Vielleicht möchten Sie dann doc.rust-lang.org/book/… erneut lesen .
Shepmaster
Vielen Dank für diese Antwort! Ich war auch durch die akzeptierte Antwort verwirrt und konnte es nicht herausfinden, bis ich Ihre Erklärung gelesen hatte.
Prgrm.celeritas
@ Shepmaster In dem Abschnitt, auf den Sie verlinken, wird nicht erwähnt, wie Makros funktionieren. Wollten Sie auf einen anderen Teil des Buches verlinken?
Detly vor