Bagaimana cara menggunakan makro di seluruh file modul?

93

Saya memiliki dua modul dalam file terpisah dalam peti yang sama, di mana peti telah macro_rulesdiaktifkan. Saya ingin menggunakan makro yang ditentukan dalam satu modul di modul lain.

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

Saat ini saya menemukan kesalahan kompilator " macro undefined: 'my_macro'" ... yang masuk akal; sistem makro berjalan sebelum sistem modul. Bagaimana cara mengatasinya?

pengguna
sumber
Shouldn; 't Anda menggunakanmodule::my_macro!()?
u_mulder
2
nope (bukan afaik) - prefiks modul dilaporkan diabaikan (menurut pesan kompilator).
pengguna

Jawaban:

131

Makro dalam peti yang sama

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

bar!();    // works

Jika Anda ingin menggunakan makro di peti yang sama, modul yang ditentukan makro Anda membutuhkan atribut #[macro_use].

Makro hanya dapat digunakan setelah ditentukan. Ini berarti ini tidak berhasil:

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

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

Makro melintasi peti

Untuk menggunakan macro_rules!makro Anda dari peti lain, makro itu sendiri membutuhkan atribut #[macro_export]. Peti pengimpor kemudian dapat mengimpor makro melalui use crate_name::macro_name;.

Peti util

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

Peti user

use util::foo;

foo!();

Perhatikan bahwa makro selalu berada di level teratas dari sebuah peti; jadi meskipun fooakan berada di dalam a mod bar {}, userpeti itu masih harus menulis use util::foo;dan tidak use util::bar::foo; .

Sebelum Rust 2018, Anda harus mengimpor makro dari peti lain dengan menambahkan atribut #[macro_use]ke extern crate util;pernyataan tersebut. Itu akan mengimpor semua makro dari util. Atau, #[macro_use(cat, dog)]dapat digunakan untuk hanya mengimpor makro catdan dog. Sintaks ini seharusnya tidak diperlukan lagi.

Informasi lebih lanjut tersedia di bab Bahasa Pemrograman Rust pada makro .

Lukas Kalbertodt
sumber
27
"Makro hanya dapat digunakan setelah ditentukan." - Ini adalah kuncinya karena Anda dapat mengalami kesalahan tersebut meskipun Anda telah melakukan semua hal lain yang disebutkan dengan benar. Misalnya, jika Anda memiliki modul macrosdan foo(yang menggunakan makro dari macros), dan Anda mencantumkannya dalam urutan abjad di lib.rs atau main.rs, foo akan dimuat sebelum makro dan kode tidak akan dikompilasi.
neverfox
7
^ tip pro - ini benar-benar membuat saya
semore_1267
3
Perhatikan juga bahwa untuk menggunakan makro secara internal, #[macro_use]atribut harus ada di setiap modul dan modul induk, dll .. hingga mencapai titik di mana Anda perlu menggunakannya.
Sepuluh
Jawaban ini tidak berhasil untuk saya. Modul yang menyatakan makro telah #[macro_use]dan dideklarasikan pertama kali di lib.rs - masih tidak berfungsi. Jawaban @ Ten membantu dan saya menambahkan #[macro_use]ke bagian atas lib.rs - kemudian berhasil. Tapi saya masih tidak yakin apa praktik terbaiknya karena saya membaca di sini bahwa "Anda tidak mengimpor makro dari modul lain; Anda mengekspor makro dari modul penentu"
Sorin Bolos
Saya selalu lupa bagaimana makro Rust bekerja dengan modul. Itu adalah sistem yang buruk, dan semoga akan ada yang lebih baik suatu hari nanti.
Hutch Moore
20

Jawaban ini sudah usang karena Rust 1.1.0-stable.


Anda perlu menambahkan #![macro_escape]di bagian atas macros.rsdan memasukkannya menggunakan mod macros;seperti yang disebutkan di Panduan Makro .

$ 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

Untuk referensi di masa mendatang,

$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)
Dogbert
sumber
Saya benar-benar merindukan atribut itu. Terima kasih!
pengguna
4
BTW, #[macro_export]atribut tidak diperlukan di sini. Ini hanya diperlukan jika makro harus diekspor ke pengguna eksternal. Jika makro hanya digunakan di dalam peti, #[macro_export]tidak diperlukan.
Vladimir Matveev
1
Terima kasih banyak atas jawabannya. Saya hanya ingin menambahkan, bahwa jika something.rsfile Anda menggunakan modul lain, misalnya dengan mod foobar;, dan foobarmodul ini menggunakan makro dari macro.rs, maka Anda harus meletakkan mod macro; sebelumnya mod foobar; agar program dapat dikompilasi. Hal kecil, tapi ini bukan IMO yang jelas.
conradkleinespel
2
(nb jawaban ini sekarang sudah usang; Saya telah menerima jawaban terbaru yang diberikan oleh Lukas)
pengguna
7

Menambahkan #![macro_use]ke bagian atas file Anda yang berisi makro akan menyebabkan semua makro ditarik ke main.rs.

Misalnya, file ini bernama node.rs:

#![macro_use]

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

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

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

Rumah utama Anda akan terlihat seperti berikut ini:

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

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

Terakhir, katakanlah Anda memiliki file bernama toad.rs yang juga membutuhkan makro berikut:

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

pub fn a_thing() {
  test!();

  node::fun_times();
}

Perhatikan bahwa setelah file ditarik ke main.rs dengan mod, sisa file Anda memiliki akses ke sana melalui usekata kunci.

Luke Dupin
sumber
Saya menambahkan lebih banyak klarifikasi. Pada rustc 1.22.1, ini berfungsi.
Luke Dupin
Apakah kamu yakin Di mana #! [Macro_use] (bukan # [macro_use]) ini didokumentasikan? Saya tidak dapat menemukannya. Tidak bisa di sini.
Markus
Ini berfungsi ketika saya mempostingnya, sistem penyertaan Rust benar-benar berantakan, sangat mungkin ini tidak berfungsi lagi.
Luke Dupin
@ Markus Perhatikan bahwa #![macro_use]pernyataan DI DALAM makro-modul, bukan di luar. The #![...]sintaks bersesuaian untuk memiliki atribut berlaku untuk lingkup yang mengandung mereka, misalnya #![feature(...)](jelas ini tidak akan masuk akal jika ditulis sebagai #[feature(...)], itu semantik akan mengharuskan compiler mengaktifkan fitur tertentu pada item tertentu dalam peti, bukan seluruh akar peti). Jadi, seperti yang dikatakan @LukeDupin, sistem modulnya berantakan, meskipun mungkin karena alasan yang berbeda dari pada pandangan pertama.
pengguna
Saya berharap jawaban ini menyebutkan bagaimana konstruksinya tidak terlalu idiomatis (selain itu, saya suka jawabannya). Terlepas dari (non) -idiomatisitasnya, itu menarik karena menempatkannya di sebelah bentuk idiomatik membuatnya sangat jelas bahwa makro berinteraksi dengan sistem modul dengan cara yang berbeda dari konstruksi biasa. Atau setidaknya mengeluarkan bau yang kuat (seperti yang baru saja ditunjukkan oleh @Markus yang mengeluh).
pengguna
2

Saya telah menemukan masalah yang sama di Rust 1.44.1, dan solusi ini berfungsi untuk versi yang lebih baru (dikenal berfungsi untuk Rust 1.7).

Katakanlah Anda memiliki proyek baru sebagai:

src/
    main.rs
    memory.rs
    chunk.rs

Di main.rs , Anda perlu memberi keterangan bahwa Anda mengimpor makro dari sumber, jika tidak, itu tidak akan berhasil untuk Anda.

#[macro_use]
mod memory;
mod chunk;

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

Jadi di memory.rs Anda dapat menentukan makro, dan Anda tidak memerlukan anotasi:

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

Akhirnya Anda bisa menggunakannya di chunk.rs , dan Anda tidak perlu menyertakan makro di sini, karena sudah selesai di main.rs:

grow_capacity!(8);

The jawaban upvoted menyebabkan kebingungan bagi saya, dengan doc ini dengan contoh , akan membantu juga.

knh190
sumber
Jawaban yang diterima secara harfiah memiliki bahwa sebagai baris pertama dari blok kode pertama: #[macro_use] mod foo {.
Shepmaster
1
@Shepmaster jawaban yang diberi suara positif memiliki definisi makro dan pernyataan impor di tempat yang sama, sehingga menyebabkan kebingungan (bagi saya). Saya menggunakan #[macro_use]definisi. Compiler tidak mengatakan itu salah tempat.
knh190
Anda mungkin ingin membaca kembali doc.rust-lang.org/book/… lalu.
Shepmaster
Terima kasih atas jawaban ini! Saya bingung dengan jawaban yang diterima juga dan tidak bisa mengetahuinya sampai saya membaca penjelasan Anda.
Prgrm.celeritas
@Shepmaster Tidak disebutkan cara kerja makro di bagian yang Anda tautkan. Apakah Anda bermaksud menautkan ke beberapa bagian lain dari buku ini?
detly