¿Cómo uso una macro en archivos de módulo?

92

Tengo dos módulos en archivos separados dentro de la misma caja, donde la caja se ha macro_ruleshabilitado. Quiero usar las macros definidas en un módulo en otro módulo.

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

Actualmente encuentro el error del compilador " macro undefined: 'my_macro'" ... que tiene sentido; el sistema de macros se ejecuta antes que el sistema de módulos. ¿Cómo soluciono eso?

usuario
fuente
No debería usarmodule::my_macro!()?
u_mulder
2
nope (no afaik): el prefijo del módulo se ignora (según el mensaje del compilador).
usuario

Respuestas:

131

Macros dentro de la misma caja

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

bar!();    // works

Si desea utilizar la macro en la misma caja, el módulo en el que está definida su macro necesita el atributo #[macro_use].

Las macros solo se pueden utilizar después de que se hayan definido. Esto significa que esto no funciona:

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

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

Macros en cajas

Para usar su macro_rules!macro de otras cajas, la macro en sí necesita el atributo #[macro_export]. La caja de importación puede importar la macro a través de use crate_name::macro_name;.

Caja util

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

Caja user

use util::foo;

foo!();

Tenga en cuenta que las macros siempre viven en el nivel superior de una caja; así que incluso si fooestuviera dentro de un mod bar {}, la usercaja todavía tendría que escribir use util::foo;y no use util::bar::foo; .

Antes de Rust 2018, tenía que importar macro de otras cajas agregando el atributo #[macro_use]a la extern crate util;declaración. Eso importaría todas las macros de util. Alternativamente, #[macro_use(cat, dog)]podría usarse para importar solo las macros caty dog. Esta sintaxis ya no debería ser necesaria.

Hay más información disponible en el capítulo sobre macros de The Rust Programming Language .

Lukas Kalbertodt
fuente
27
"Las macros solo se pueden utilizar después de haberlas definido". - Esto es clave porque puede encontrarse con ese error incluso cuando haya hecho todas las otras cosas mencionadas correctamente. Por ejemplo, si tiene módulos macrosy foo(que usa una macro de macros), y los enumera en orden alfabético en sus lib.rs o main.rs, foo se cargará antes que las macros y el código no se compilará.
neverfox
7
^ consejo profesional - esto me
atrapó
3
También tenga en cuenta que para usar macros internamente, el #[macro_use]atributo debe estar en cada módulo y módulo principal, etc. hasta que llegue al punto en el que necesite usarlo.
Diez
Esta respuesta no funcionó para mí. El módulo que declaró la macro tenía #[macro_use]y se declaró primero en lib.rs - todavía no funcionó. La respuesta de @ Ten ayudó y agregué #[macro_use]a la parte superior de lib.rs, luego funcionó. Pero todavía no estoy seguro de cuál es la mejor práctica, ya que leí aquí que "No importa macros de otros módulos; exporta la macro desde el módulo de definición"
Sorin Bolos
Siempre olvido cómo funcionan las macros de Rust con los módulos. Es un sistema terrible y, con suerte, algún día habrá uno mejor.
Hutch Moore
20

Esta respuesta está desactualizada a partir de Rust 1.1.0-estable.


Debe agregarlo #![macro_escape]en la parte superior macros.rse incluirlo usando mod macros;como se menciona en la Guía de 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

Para futura referencia,

$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)
Dogbert
fuente
Me había perdido por completo ese atributo. ¡Gracias!
usuario
4
Por cierto, el #[macro_export]atributo es innecesario aquí. Solo es necesario si la macro debe exportarse a usuarios externos de cajas. Si la macro solo se usa dentro de la caja, #[macro_export]no es necesaria.
Vladimir Matveev
1
Muchas gracias por la respuesta. Solo quiero agregar que si su something.rsarchivo usa otros módulos, por ejemplo mod foobar;, con , y este foobarmódulo usa las macros de macro.rs, entonces debe poner mod macro; antes mod foobar; para que el programa se compile. Algo menor, pero esto no es una OMI obvia.
conradkleinespel
2
(Nota: esta respuesta ahora está desactualizada; he aceptado la respuesta actualizada dada por Lukas)
usuario
7

Agregar #![macro_use]a la parte superior de su archivo que contiene macros hará que todas las macros se incorporen a main.rs.

Por ejemplo, supongamos que este archivo se llama node.rs:

#![macro_use]

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

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

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

Su main.rs se vería en algún momento como el siguiente:

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

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

Finalmente, digamos que tiene un archivo llamado toad.rs que también requiere estas macros:

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

pub fn a_thing() {
  test!();

  node::fun_times();
}

Tenga en cuenta que una vez que los archivos se introducen en main.rs con mod, el resto de sus archivos tienen acceso a ellos a través de la usepalabra clave.

Luke Dupin
fuente
Agregué más aclaraciones. A partir de rustc 1.22.1, esto funciona.
Luke Dupin
¿Estás seguro? ¿Dónde está documentado #! [Macro_use] (no # [macro_use])? No lo encuentro. Aquí no funciona.
Markus
Esto funcionó cuando lo publiqué, el sistema de inclusión de Rust es un desastre tan terrible, es completamente posible que esto ya no funcione.
Luke Dupin
@Markus Tenga en cuenta que la #![macro_use]declaración está DENTRO del macro-módulo, no fuera. La #![...]sintaxis corresponde a tener atributos que se aplican a los ámbitos que los contienen, por ejemplo #![feature(...)](obviamente, esto no tendría sentido si se escribiera como #[feature(...)]; requeriría semánticamente que el compilador habilite ciertas características en elementos específicos en una caja, en lugar de la caja raíz completa). Entonces, como dijo @LukeDupin, el sistema de módulos es un desastre, aunque quizás por una razón diferente a la primera vista.
usuario
Desearía que esta respuesta mencionara cómo la construcción no es exactamente idiomática (aparte de eso, me gusta la respuesta). A pesar de su (no) -idiomaticidad, es interesante porque colocarlo junto a la forma idiomática hace que sea dolorosamente obvio que las macros interactúan con el sistema de módulos de una manera diferente a las construcciones habituales. O al menos desprende un olor fuerte (como acaba de demostrar @Markus teniendo una queja).
usuario
2

Me encontré con el mismo problema en Rust 1.44.1, y esta solución funciona para versiones posteriores (se sabe que funciona para Rust 1.7).

Digamos que tiene un nuevo proyecto como:

src/
    main.rs
    memory.rs
    chunk.rs

En main.rs , debe anotar que está importando macros desde la fuente, de lo contrario, no funcionará.

#[macro_use]
mod memory;
mod chunk;

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

Entonces, en memory.rs puede definir las macros y no necesita anotaciones:

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

Finalmente, puede usarlo en chunk.rs , y no necesita incluir la macro aquí, porque está hecho en main.rs:

grow_capacity!(8);

La respuesta votada a favor me causó confusión, con este documento como ejemplo , también sería útil.

knh190
fuente
La respuesta aceptada literalmente tiene que a medida que las primeras líneas del primer bloque de código: #[macro_use] mod foo {.
Shepmaster
1
@Shepmaster, la respuesta votada tiene una definición de macros y la declaración de importación en el mismo lugar, por lo que causó confusión (para mí). Estaba usando #[macro_use]en definición. El compilador no dice que esté fuera de lugar.
knh190
Es posible que desee volver a leer doc.rust-lang.org/book/… entonces.
Shepmaster
¡Gracias por esta respuesta! También estaba confundido por la respuesta aceptada y no pude entenderlo hasta que leí su explicación.
Prgrm.celeritas
@Shepmaster No se menciona cómo funcionan las macros en la sección a la que enlaza. ¿Querías enlazar a alguna otra parte del libro?
detly