Question
I have two modules in separate files within the same Rust crate. The crate uses macro_rules!, and I want to use a macro defined in one module file from another module file.
For example:
// macros.rs
macro_rules! my_macro {
() => {
println!("Hello from macro");
};
}
// something.rs
use macros;
// use macros::my_macro; // this does not work for macro_rules!
fn demo() {
my_macro!();
}
Right now I get a compiler error like cannot find macro 'my_macro' in this scope or macro undefined: 'my_macro'.
Since macros are handled differently from normal items such as functions or structs, how can I make a macro_rules! macro defined in one module available in another module file within the same crate?
Short Answer
By the end of this page, you will understand how macro_rules! visibility works in Rust, why macros behave differently from normal imports, and the main ways to share a macro between module files in the same crate. You will also see practical patterns, common mistakes, and the modern preferred approach.
Concept
macro_rules! macros in Rust are not exactly like functions, structs, or modules. A normal item is usually accessed through the module system with paths such as crate::utils::helper(). Macros have their own scoping and visibility rules.
The key idea is:
- Functions and structs are resolved through the normal module namespace.
macro_rules!macros historically use a separate macro namespace and have special import/export behavior.
This matters because code like this does not behave the same way as a function import:
use macros::my_macro;
For older-style macro_rules! macros, that often does not work the way beginners expect.
Why this matters
In real Rust projects, macros are often used to:
- reduce repetitive code
- build custom logging or assertions
- generate implementations
- create small domain-specific syntax
If you split your crate into multiple files, you need to know how macro visibility works, or the compiler will say the macro is not in scope.
The main approaches
There are two common patterns when working with macro_rules! across files:
-
Bring macros into scope with
#[macro_use]
Mental Model
Think of Rust macros as special tools stored in a toolbox that is separate from your normal code shelves.
- Your module system is like shelves labeled
utils,models, andapi. - Your macros are like special tools that do not automatically sit on those shelves in the same way.
So if you put a hammer in a room called macros.rs, that does not mean another room can automatically grab it with a normal shelf lookup.
You need to either:
- place the tool into a shared toolbox everyone can use (
#[macro_use]), or - register it so it can be found from the main entrance (
#[macro_export]andcrate::my_macro!())
That is why macros feel different from functions: they follow special access rules.
Syntax and Examples
Basic pattern with #[macro_use]
This is a traditional way to make macros from one module available to the rest of the crate.
// main.rs or lib.rs
#[macro_use]
mod macros;
mod something;
// macros.rs
macro_rules! my_macro {
() => {
println!("Hello from macro");
};
}
// something.rs
pub fn demo() {
my_macro!();
}
Explanation
#[macro_use] mod macros;tells Rust to load macros from that module into scope.- After that, sibling modules such as
something.rscan usemy_macro!().
Pattern with #[macro_export]
Another common approach is to export the macro to the crate root.
// lib.rs or main.rs
macros;
something;
Step by Step Execution
Consider this example:
// lib.rs
#[macro_use]
mod macros;
mod something;
// macros.rs
macro_rules! my_macro {
() => {
println!("Hello");
};
}
// something.rs
pub fn demo() {
my_macro!();
}
What happens step by step
- Rust starts reading
lib.rs. - It sees
#[macro_use] mod macros;. - The
macrosmodule is loaded, and itsmacro_rules!macros are made available. - Rust then processes
mod something;. - Inside
something.rs, the callmy_macro!()is now recognized. - The macro expands into:
println!("Hello");
Real World Use Cases
Macros shared across module files are useful when a project has repeated patterns.
Common real uses
-
Custom logging macros
- Example:
debug_db!()to print SQL queries only in development.
- Example:
-
Assertion helpers for tests
- Example:
assert_contains!()used in multiple test modules.
- Example:
-
Boilerplate reduction
- Example: generating repeated implementations for multiple types.
-
Consistent error creation
- Example: a macro that creates formatted errors with file and line information.
-
Domain-specific shortcuts
- Example: building route definitions, SQL fragments, or event declarations.
Example scenario
A crate might contain:
macros.rsfor reusable macrosparser.rsthat uses a macro to reduce repetitive parsing codetests.rsthat uses custom assertion macros
This keeps repeated logic in one place while allowing many modules to use it.
Real Codebase Usage
In real Rust codebases, developers usually organize macros in a dedicated module such as macros.rs and make them available in one of a few clear ways.
Common patterns
1. Central macro module
#[macro_use]
mod macros;
This is common in older codebases and internal crates where macros are used widely.
2. Export to crate root
#[macro_export]
macro_rules! my_macro {
() => { /* ... */ };
}
Then use:
crate::my_macro!();
This is explicit and works well when the macro should be accessible broadly.
3. Helper macros for validation or early returns
Macros are often used to simplify repetitive checks such as:
macro_rules! ensure {
($cond:expr, $msg:expr) => {
if !$cond {
return Err($msg.into());
}
};
}
This can reduce repeated validation code in many modules.
4. Test support macros
Projects often place assertion helpers in a shared macro file so multiple test modules can use them.
Common Mistakes
1. Trying to import a macro_rules! macro like a normal item
Broken example:
use macros::my_macro;
Why it fails:
macro_rules!macros are not always imported like functions or structs.- Older macro behavior especially differs from normal item imports.
How to fix it:
- Use
#[macro_use] mod macros;, or - Use
#[macro_export]and callcrate::my_macro!()
2. Expecting #[macro_export] to keep the macro under the module path
Broken assumption:
crate::macros::my_macro!();
Why it fails:
#[macro_export]exports the macro at the crate root.
Correct usage:
crate::my_macro!();
3. Declaring modules in the wrong order
Broken example:
Comparisons
#[macro_use] vs #[macro_export]
| Approach | What it does | Best for | Trade-off |
|---|---|---|---|
#[macro_use] mod macros; | Brings macros from a module into scope | Internal crate-wide macro sharing | Less explicit |
#[macro_export] | Exports macro to crate root | Macros used broadly or externally | Path can be surprising |
Macros vs functions
| Feature | macro_rules! macro | Function |
|---|---|---|
| Resolved through normal module imports | Not always |
Cheat Sheet
Quick reference
Define a macro
macro_rules! my_macro {
() => {
println!("Hello");
};
}
Make macros from macros.rs available in the crate
#[macro_use]
mod macros;
Export a macro to crate root
#[macro_export]
macro_rules! my_macro {
() => {
println!("Hello");
};
}
Use it from the same crate:
crate::my_macro!();
Important rules
macro_rules!macros are not always used like normal imported items.#[macro_export]places the macro at the crate root.- Module declaration order can matter.
- If many files use a macro, put it in
macros.rsand load it early. - Prefer functions unless you need macro behavior.
Typical setup
FAQ
Why can I not import a macro_rules! macro like a normal function?
Because macros use special scoping and resolution rules. They are not ordinary module items in the same way functions are.
What does #[macro_use] do in Rust?
It brings macros from a module into scope so they can be used elsewhere in the crate without normal item imports.
What does #[macro_export] do?
It exports the macro to the crate root, which means you can usually call it as crate::my_macro!() inside the same crate.
Should I use #[macro_use] or #[macro_export]?
For internal crate organization, #[macro_use] is a common older pattern. For explicit crate-root access, #[macro_export] is often clearer.
Why does crate::macros::my_macro!() not work?
Because #[macro_export] exports the macro at the crate root, not under the original module path.
Does module order matter for macros?
Yes. If the macro is not made available before another module tries to use it, you can get scope errors.
Should I write a macro or a function?
Use a function unless you need syntax-level code generation, flexible token matching, or macro-specific behavior.
Mini Project
Description
Create a small Rust crate with a shared macro file and another module that uses the macro. This demonstrates how to organize macro_rules! macros across multiple files in a practical project structure.
Goal
Build a crate where a macro defined in macros.rs is successfully used inside something.rs.
Requirements
Create a macros.rs file that defines one reusable macro_rules! macro.
Declare the macro module from lib.rs or main.rs so the macro is available.
Create a second module file that calls the macro.
Run the project and confirm the macro expands and prints output.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.
Convert an Integer to a String in Rust
Learn the current Rust way to convert integers to strings, why `to_str()` no longer works, and when to use `to_string()` or `format!`.