Jak używać makra w plikach modułów?

92

Mam dwa moduły w osobnych plikach w tej samej skrzynce, w której skrzynia została macro_ruleswłączona. Chcę użyć makr zdefiniowanych w jednym module w innym module.

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

Aktualnie trafiam na błąd kompilatora " macro undefined: 'my_macro'" ... który ma sens; system makr działa przed systemem modułu. Jak mam to obejść?

użytkownik
źródło
Powinienem; 't używaszmodule::my_macro!()?
u_mulder
2
nope (not afaik) - prefiks modułu jest podobno ignorowany (zgodnie z komunikatem kompilatora).
użytkownik

Odpowiedzi:

131

Makra w tej samej skrzynce

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

bar!();    // works

Jeśli chcesz użyć makra w tej samej skrzynce, moduł, w którym zdefiniowane jest makro, potrzebuje atrybutu #[macro_use].

Makra można używać tylko po ich zdefiniowaniu. Oznacza to, że to nie działa:

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

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

Makra w skrzyniach

Aby użyć macro_rules!makra z innych skrzynek, samo makro potrzebuje atrybutu #[macro_export]. Skrzynka importująca może następnie zaimportować makro przez use crate_name::macro_name;.

Skrzynia util

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

Skrzynia user

use util::foo;

foo!();

Zwróć uwagę, że makra zawsze znajdują się na najwyższym poziomie skrzynki; więc nawet gdyby foobył wewnątrz a mod bar {}, userskrzynia nadal musiałaby pisać, use util::foo;a nie use util::bar::foo; .

Przed Rust 2018 trzeba było importować makro z innych skrzynek, dodając atrybut #[macro_use]do extern crate util;instrukcji. Spowoduje to zaimportowanie wszystkich makr z util. Alternatywnie #[macro_use(cat, dog)]można użyć tylko do importowania makr cati dog. Ta składnia nie powinna być już potrzebna.

Więcej informacji można znaleźć w rozdziale poświęconym makrom w języku programowania Rust .

Lukas Kalbertodt
źródło
27
„Makra można używać tylko po ich zdefiniowaniu”. - Jest to kluczowe, ponieważ możesz napotkać ten błąd, nawet jeśli wykonałeś wszystkie inne wymienione czynności poprawnie. Na przykład, jeśli masz moduły macrosi foo(które używa makra z macros) i umieścisz je w porządku alfabetycznym w swoim lib.rs lub main.rs, foo zostanie załadowany przed makrami i kod nie zostanie skompilowany.
neverfox
7
^ pro wskazówka - to mnie
dopadło
3
Zauważ również, że aby używać makr wewnętrznie, #[macro_use]atrybut powinien znajdować się na każdym module i module nadrzędnym itp., Aż osiągnie punkt, w którym musisz go użyć.
10
Ta odpowiedź mi nie odpowiada. Moduł, który zadeklarował makro, miał #[macro_use]i został zadeklarowany jako pierwszy w lib.rs - nadal nie działał. Odpowiedź @ Ten pomogła i dodałem #[macro_use]na początek lib.rs - wtedy zadziałało. Ale nadal nie jestem pewien, jaka jest najlepsza praktyka, ponieważ przeczytałem tutaj, że „Nie importujesz makr z innych modułów; eksportujesz makro z modułu definiującego”
Sorin Bolos
Zawsze zapominam, jak makra Rusta współpracują z modułami. To okropny system i mam nadzieję, że kiedyś będzie lepszy.
Hutch Moore
20

Ta odpowiedź jest nieaktualna w wersji stabilnej dla Rust 1.1.0.


Musisz dodać go #![macro_escape]na górze macros.rsi dołączyć, używając, mod macros;jak wspomniano w Przewodniku po makrach .

$ 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

Dla przyszłego odniesienia,

$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)
Dogbert
źródło
Całkowicie brakowało mi tego atrybutu. Dzięki!
użytkownik
4
BTW, #[macro_export]atrybut jest tutaj niepotrzebny. Jest potrzebny tylko wtedy, gdy makro ma zostać wyeksportowane do zewnętrznych użytkowników skrzynki. Jeśli makro jest używane tylko w skrzyni, #[macro_export]nie jest potrzebne.
Vladimir Matveev
1
Bardzo dziękuję za odpowiedź. Dodam tylko, że jeśli twój something.rsplik używa innych modułów, na przykład z mod foobar;, a ten foobarmoduł używa makr z macro.rs, to musisz wstawić mod macro; wcześniej, mod foobar; aby program się skompilował. Drobna rzecz, ale nie jest to oczywista IMO.
conradkleinespel
2
(uwaga: ta odpowiedź jest już nieaktualna; zaakceptowałem aktualną odpowiedź udzieloną przez Lukasa)
użytkownik
7

Dodanie #![macro_use]na początek pliku zawierającego makra spowoduje pobranie wszystkich makr do main.rs.

Na przykład załóżmy, że ten plik nazywa się node.rs:

#![macro_use]

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

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

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

Twój plik main.rs wyglądałby kiedyś tak:

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

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

Na koniec powiedzmy, że masz plik o nazwie toad.rs, który również wymaga tych makr:

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

pub fn a_thing() {
  test!();

  node::fun_times();
}

Zwróć uwagę, że po ściągnięciu plików do main.rs z mod, pozostałe pliki mają do nich dostęp poprzez usesłowo kluczowe.

Luke Dupin
źródło
Dodałem więcej wyjaśnień. Od rustc 1.22.1 to działa.
Luke Dupin
Jesteś pewny? Gdzie jest to #! [Macro_use] (nie # [macro_use]) udokumentowane? Nie mogę tego znaleźć. Tutaj to nie działa.
Markus,
To zadziałało, kiedy go opublikowałem, system dołączania Rusta jest taki okropny, jest całkiem możliwe, że to już nie działa.
Luke Dupin
@Markus Zauważ, że #![macro_use]instrukcja znajduje się WEWNĄTRZ makromodułu , a nie na zewnątrz. Do #![...]odpowiada składni do atrybutów mających zastosowanie do ich zakresów zawierających np #![feature(...)](oczywiście to nie ma sensu, jeśli napisany jako #[feature(...)], że to semantycznie wymagają kompilator włączyć niektóre funkcje na poszczególnych elementów w skrzynce, a nie całej skrzyni root). Czyli, jak powiedział @LukeDupin, system modułów to bałagan, choć może z innego powodu niż na pierwszy rzut oka.
użytkownik
Żałuję, że ta odpowiedź nie wspomina, że ​​konstrukcja nie jest dokładnie idiomatyczna (poza tym podoba mi się odpowiedź). Pomimo swojej (nie) -diomatyczności jest to interesujące, ponieważ umieszczenie go obok idiomatycznej formy sprawia, że ​​boleśnie staje się oczywiste, że makra oddziałują z systemem modułowym w inny sposób niż zwykłe konstrukcje. Lub przynajmniej wydziela silny zapach (co właśnie zademonstrował @Markus, który ma z tym problem).
użytkownik
2

I natknąłem się na ten sam problem w Rust 1.44.1, a to rozwiązanie działa na nowszych wersjach (nazywanych pracujących dla Rust 1.7).

Załóżmy, że masz nowy projekt jako:

src/
    main.rs
    memory.rs
    chunk.rs

W main.rs musisz dodać adnotację, że importujesz makra ze źródła, w przeciwnym razie nie będzie to dla Ciebie przydatne.

#[macro_use]
mod memory;
mod chunk;

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

Więc w memory.rs możesz zdefiniować makra i nie potrzebujesz adnotacji:

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

Wreszcie możesz go użyć w chunk.rs i nie musisz tutaj umieszczać makra, ponieważ jest to zrobione w main.rs:

grow_capacity!(8);

Upvoted odpowiedź spowodowała zamieszanie na mnie, z tego dokumentu przez przykład , byłoby zbyt pomocne.

knh190
źródło
Odpowiedź zaakceptowany dosłownie ma, że jako pierwsze linie pierwszego bloku kodu: #[macro_use] mod foo {.
Shepmaster
1
@Shepmaster odpowiedź za głosowaniem zawiera definicję makr i instrukcję import w tym samym miejscu, więc spowodowała zamieszanie (dla mnie). Używałem #[macro_use]w definicji. Kompilator nie mówi, że jest źle umieszczony.
knh190
Możesz wtedy ponownie przeczytać doc.rust-lang.org/book/… .
Shepmaster
Dziękuję za tę odpowiedź! Byłem również zdezorientowany przyjętą odpowiedzią i nie mogłem tego zrozumieć, dopóki nie przeczytałem twojego wyjaśnienia.
Prgrm.celeritas
@Shepmaster Nie ma żadnej wzmianki o działaniu makr w sekcji, do której prowadzi łącze. Czy chodziło Ci o link do innej części książki?
detly