Como faço para usar uma macro em arquivos de módulo?


105

Tenho dois módulos em arquivos separados dentro da mesma caixa, onde a caixa foi macro_rulesativada. Quero usar as macros definidas em um módulo em outro 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?

Atualmente, encontro o erro do compilador " macro undefined: 'my_macro'" ... o que faz sentido; o sistema macro é executado antes do sistema de módulo. Como faço para contornar isso?


Você não module::my_macro!()?
deveria

2
nope (not afaik) - o prefixo do módulo é supostamente ignorado (de acordo com a mensagem do compilador).
usuário

Respostas:


144

Macros dentro da mesma caixa

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

bar!();    // works

Se você quiser usar a macro na mesma caixa, o módulo em que sua macro está definida precisa do atributo #[macro_use].

As macros só podem ser usadas depois de definidas. Isso significa que isso não funciona:

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

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

Macros em caixas

Para usar sua macro_rules!macro de outras caixas, a própria macro precisa do atributo #[macro_export]. A caixa de importação pode então importar a macro via use crate_name::macro_name;.

Caixote util

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

Caixote user

use util::foo;

foo!();

Observe que as macros sempre vivem no nível superior de uma caixa; então, mesmo fooque estivesse dentro de um mod bar {}, a usercaixa ainda teria que escrever use util::foo;e não use util::bar::foo; .

Antes do Rust 2018, você tinha que importar macro de outras caixas adicionando o atributo #[macro_use]à extern crate util;instrução. Isso importaria todas as macros de util. Como alternativa, #[macro_use(cat, dog)]pode ser usado apenas para importar as macros cate dog. Esta sintaxe não deve ser mais necessária.

Mais informações estão disponíveis no capítulo The Rust Programming Language sobre macros .


32
"As macros só podem ser usadas depois de definidas." - Isso é fundamental porque você pode encontrar esse erro mesmo depois de fazer todas as outras coisas mencionadas corretamente. Por exemplo, se você tiver módulos macrose foo(que usa uma macro de macros) e os listar em ordem alfabética em seu lib.rs ou main.rs, foo será carregado antes das macros e o código não será compilado.
neverfox de

9
^ dica profissional - isso me pegou totalmente
semore_1267

6
Observe também que, para usar macros internamente, o #[macro_use]atributo deve estar em cada módulo e módulo pai, etc. até atingir o ponto em que você precisa usá-lo.
Dez

1
Essa resposta não funcionou para mim. O módulo que declarou a macro tinha #[macro_use]e foi declarado primeiro em lib.rs - ainda não funcionou. A resposta do @ Ten ajudou e acrescentei #[macro_use]ao topo do lib.rs - então funcionou. Mas ainda não tenho certeza de qual é a prática recomendada, já que li aqui que "Você não importa macros de outros módulos; você exporta a macro do módulo de definição"
Sorin Bolos

Sempre esqueço como as macros do Rust funcionam com módulos. É um sistema horrível e, com sorte, haverá um melhor algum dia.
Hutch Moore

21

Esta resposta está desatualizada no Rust 1.1.0-stable.


Você precisa adicionar #![macro_escape]no topo macros.rse incluí-lo usando mod macros;conforme mencionado no Guia 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 referência futura,

$ rustc -v
rustc 0.13.0-dev (2790505c1 2014-11-03 14:17:26 +0000)

Eu perdi totalmente esse atributo. Obrigado!
usuário

4
BTW, o #[macro_export]atributo é desnecessário aqui. É necessário apenas se a macro for exportada para usuários externos da caixa. Se a macro for usada apenas dentro da caixa, #[macro_export]não é necessária.
Vladimir Matveev

1
Muito obrigado pela resposta. Só quero acrescentar que se o seu something.rsarquivo usa outros módulos, por exemplo com mod foobar;, e este foobarmódulo usa as macros de macro.rs, então você deve colocar mod macro; antes mod foobar; para que o programa compile. Coisa menor, mas esta não é uma IMO óbvia.
conradkleinespel

2
(nota: esta resposta agora está desatualizada; aceitei a resposta atualizada fornecida por Lukas)
usuário

9

Adicionar #![macro_use]ao início do arquivo que contém macros fará com que todas as macros sejam puxadas para main.rs.

Por exemplo, vamos supor que este arquivo se chame node.rs:

#![macro_use]

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

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

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

Em algum momento, seu main.rs se pareceria com o seguinte:

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

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

Finalmente, digamos que você tenha um arquivo chamado toad.rs que também requer estas macros:

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

pub fn a_thing() {
  test!();

  node::fun_times();
}

Observe que, uma vez que os arquivos são puxados para main.rs com mod, o restante dos seus arquivos tem acesso a eles por meio da usepalavra - chave.


Eu adicionei mais esclarecimentos. A partir do rustc 1.22.1, isso funciona.
Luke Dupin

Você tem certeza? Onde este #! [Macro_use] (não # [macro_use]) está documentado? Eu não consigo encontrar. Não funciona aqui.
Markus

Isso funcionou quando eu o postei, o sistema de inclusão do Rust é uma bagunça terrível, é perfeitamente possível que isso não funcione mais.
Luke Dupin

@Markus Observe que a #![macro_use]instrução está DENTRO do macro-módulo, não fora. A #![...]sintaxe corresponde a ter atributos aplicados aos escopos que os contêm, por exemplo #![feature(...)](obviamente, isso não faria sentido se escrito como #[feature(...)]; semanticamente exigiria que o compilador habilitasse certos recursos em itens específicos em uma caixa, ao invés de toda a caixa raiz). Então, como @LukeDupin disse, o sistema de módulos está uma bagunça, embora talvez por um motivo diferente do que à primeira vista.
usuário

Eu gostaria que esta resposta mencionasse como a construção não é exatamente idiomática (isso aparte, eu gosto da resposta). Apesar de sua (não) -idiomaticidade, é interessante porque colocá-lo próximo à forma idiomática torna dolorosamente óbvio que as macros interagem com o sistema de módulos de uma maneira diferente das construções usuais. Ou pelo menos exala um cheiro forte (como foi demonstrado por @Markus ter uma reclamação dele).
usuário

3

Eu me deparei com o mesmo problema no Rust 1.44.1, e esta solução funciona para versões posteriores (funcionamento conhecido para o Rust 1.7).

Digamos que você tenha um novo projeto como:

src/
    main.rs
    memory.rs
    chunk.rs

Em main.rs , você precisa anotar que está importando macros da fonte, caso contrário, não servirá para você.

#[macro_use]
mod memory;
mod chunk;

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

Portanto, em memory.rs você pode definir as macros e não precisa de anotações:

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

Por fim, você pode usá-lo em chunk.rs e não precisa incluir a macro aqui, porque é feito em main.rs:

grow_capacity!(8);

A resposta votada causou confusão para mim, com este documento por exemplo , seria útil também.


A resposta aceita literalmente tem que, como as primeiras linhas do primeiro bloco de código: #[macro_use] mod foo {.
Shepmaster

2
@Shepmaster a resposta votada tem definição de macros e a instrução de importação no mesmo lugar, por isso causou confusão (para mim). Eu estava usando #[macro_use]em definição. O compilador não diz que está fora do lugar.
knh190

Você pode querer reler doc.rust-lang.org/book/… então.
Shepmaster

Obrigado por esta resposta! Eu também estava confuso com a resposta aceita e não consegui descobrir até ler sua explicação.
Prgrm.celeritas

1
Também não se esqueça de ter a ordem correta de modnos main.rs. Se você tiver feito isso mod chunk; mod memory;, a chamada da macro memory.rsfalhará.
ineiti
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.