The Problem
You hit compile, but Rust claims your macro doesn't exist. This usually happens when calling a popular macro like serde_json::json! or a local macro_rules! block. Because macros are expanded before the compiler resolves the rest of your code, they follow stricter scoping rules than standard functions.
// This snippet triggers the error
fn main() {
// Error: cannot find macro `json` in this scope
let my_data = json!({ "key": "value" });
}
Common Causes and Fixes
1. Missing Imports for External Macros
Since the release of Rust 1.31 (Edition 2018), macros act more like standard items. You no longer need to wrap everything in legacy extern crate blocks. However, you still need to bring the macro into the current file's scope.
The Fix: Use an explicit use statement at the top of your module. This is the cleanest way to handle dependencies like serde or tokio.
use serde_json::json;
fn main() {
let my_data = json!({ "status": 200 }); // Works perfectly
}
2. The "Top-to-Bottom" Definition Rule
Rust processes macro_rules! sequentially. If you define a macro at the end of main.rs but try to call it in a function at the top, the compiler will fail. It hasn't reached the definition yet.
// ❌ This will fail
fn main() {
say_hello!();
}
macro_rules! say_hello {
() => { println!("Hello!"); };
}
The Fix: Move your macro definition above any code that calls it. In Rust, order matters for macros within the same file.
macro_rules! say_hello {
() => { println!("Hello!"); };
}
fn main() {
say_hello!(); // Fixed: The compiler saw the definition first
}
3. Exporting Macros Across Module Boundaries
Standard visibility keywords like pub don't apply to macro_rules!. If you define a macro in src/utils.rs and want to use it in src/main.rs, you need a different approach.
The Fix: Apply the #[macro_export] attribute. This does two things: it makes the macro public and places it at the root of your crate.
// src/my_macros.rs
#[macro_export]
macro_rules! debug_log {
($msg:expr) => { println!("[DEBUG]: {}", $msg); };
}
// src/main.rs
mod my_macros;
fn main() {
// Access it via the crate root
crate::debug_log!("System initialized");
}
4. Using #[macro_use] for Internal Modules
If you prefer not to use the crate:: prefix every time, you can pull all macros from a child module into the parent scope using #[macro_use] on the module declaration.
// src/main.rs
#[macro_use]
mod my_macros;
fn main() {
// Now available without a prefix
debug_log!("This works locally");
}
Verification
Check your progress by running a quick check in your terminal:
cargo check
This command is faster than a full build because it skips code generation. If you use VS Code, the rust-analyzer extension should clear the red error highlights as soon as you save the file with the correct use or #[macro_export] statement.
Quick Summary
- Lexical Order: Within one file, define the macro before you call it.
- Visibility: Use
#[macro_export]to share macros between different files. - Modern Style: Prefer
use crate_name::macro_nameover legacy#[macro_use]on external crates to keep your namespace organized.

