Search code examples
modulerustrust-macros

How do I use a macro across module files?


I have two modules in separate files within the same crate, where the crate has macro_rules enabled. I want to use the macros defined in one module in another 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?

I currently hit the compiler error "macro undefined: 'my_macro'"... which makes sense; the macro system runs before the module system. How do I work around that?


Solution

  • Macros within the same crate

    New method (since Rust 1.32, 2019-01-17)

    foo::bar!();  // works
    
    mod foo {
        macro_rules! bar {
            () => ()
        }
    
        pub(crate) use bar;    // <-- the trick
    }
    
    foo::bar!();  // works
    

    With the pub use, the macro can be used and imported like any other item. And unlike the older method, this does not rely on source code order, so you can use the macro before (source code order) it has been defined.

    Old method

    bar!();   // Does not work! Relies on source code order!
    
    #[macro_use]
    mod foo {
        macro_rules! bar {
            () => ()
        }
    }
    
    bar!();    // works
    

    If you want to use the macro in the same crate, the module your macro is defined in needs the attribute #[macro_use]. Note that macros can only be used after they have been defined!



    Macros across crates

    Crate util

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

    Crate user

    use util::foo;
    
    foo!();
    

    Note that with this method, macros always live at the top-level of a crate! So even if foo would be inside a mod bar {}, the user crate would still have to write use util::foo; and not use util::bar::foo;. By using pub use, you can export a macro from a module of your crate (in addition to it being exported at the root).

    Before Rust 2018, you had to import macro from other crates by adding the attribute #[macro_use] to the extern crate util; statement. That would import all macros from util. This syntax should not be necessary anymore.