Mam dwa moduły w osobnych plikach w tej samej skrzynce, w której skrzynia została macro_rules
włą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ść?
module
rust
rust-macros
użytkownik
źródło
źródło
module::my_macro!()?
Odpowiedzi:
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 przezuse 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
foo
był wewnątrz amod bar {}
,user
skrzynia nadal musiałaby pisać,use util::foo;
a nieuse util::bar::foo;
.Przed Rust 2018 trzeba było importować makro z innych skrzynek, dodając atrybut
#[macro_use]
doextern crate util;
instrukcji. Spowoduje to zaimportowanie wszystkich makr zutil
. Alternatywnie#[macro_use(cat, dog)]
można użyć tylko do importowania makrcat
idog
. 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 .
źródło
macros
ifoo
(które używa makra zmacros
) 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.#[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ć.#[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”Ta odpowiedź jest nieaktualna w wersji stabilnej dla Rust 1.1.0.
Musisz dodać go
#![macro_escape]
na górzemacros.rs
i 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)
źródło
#[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.something.rs
plik używa innych modułów, na przykład zmod foobar;
, a tenfoobar
moduł używa makr zmacro.rs
, to musisz wstawićmod macro;
wcześniej,mod foobar;
aby program się skompilował. Drobna rzecz, ale nie jest to oczywista IMO.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 poprzezuse
słowo kluczowe.źródło
#![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.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:
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.
źródło
#[macro_use] mod foo {
.#[macro_use]
w definicji. Kompilator nie mówi, że jest źle umieszczony.