When the macro rejects your tokens
You write a macro call that looks perfectly reasonable. You pass the arguments, you add the exclamation mark, you hit build. The compiler stops you with a wall of text: error: no rules expected the token. You stare at the code. The arguments are there. The syntax looks right. The compiler disagrees.
This error happens because macros are not functions. They do not just accept arguments and run code. They match patterns. When you invoke a macro, the compiler takes the tokens inside the call and tries to fit them into the grammar rules you defined. If the tokens don't match any rule, the macro refuses to expand. The error means the pattern match failed. Your call shape does not fit the macro's expectations.
Macros are pattern matchers, not functions
Functions have a signature. The compiler checks types and argument counts. Macros have a set of rules. Each rule defines a pattern of tokens and a template for the output. When you call a macro, the compiler tokenizes the input and attempts to match it against the patterns.
The error "no rules expected the token" occurs when the compiler has tried every rule in the macro and none of them matched the token sequence you provided. It is a syntax-level rejection, not a type error. Macros run before type checking. The compiler is telling you that the shape of your tokens is wrong, not that the types are incompatible.
Think of a macro like a form with strict validation. You fill in the boxes, but the clerk rejects the form because you used a red pen instead of a black one, or you skipped a required checkbox. The clerk doesn't care about the content of your answers. The clerk only cares that the form matches the required format. Your macro definition defines the format. Your invocation provides the filled form. If the format doesn't match, the transaction fails.
Minimal example: delimiters and fragments
Macros care about delimiters and fragment specifiers. Delimiters are the brackets that wrap the macro arguments: parentheses (), braces {}, or brackets []. Fragment specifiers like :ident, :expr, or :block define what kind of token must appear in a specific position.
macro_rules! greet {
// Rule: expects an identifier inside parentheses.
// The pattern demands `(`, then an identifier, then `)`.
($name:ident) => {
println!("Hello, {}!", $name);
};
}
fn main() {
// This works: "alice" is an identifier, and we used parentheses.
greet!(alice);
// This fails: we used braces instead of parentheses.
// The rule expects `(`, but the call provides `{`.
// greet! { alice };
// Error: no rules expected the token `{`
// This fails: we passed a string literal, not an identifier.
// The rule expects `:ident`, but the call provides a string.
// greet!("alice");
// Error: no rules expected the token `"`
}
The error message points to the specific token that broke the match. If you use the wrong delimiters, the error points to the opening bracket. If you pass the wrong token type, the error points to the token that doesn't fit the fragment specifier.
Check the delimiters first. A mismatch between () and {} is the most common cause of this error. The macro definition and the invocation must use the exact same delimiters.
What happens at compile time
When the compiler encounters a macro call, it performs a sequence of steps. First, it tokenizes the input. The source code becomes a stream of tokens like identifiers, literals, operators, and delimiters. Next, it looks up the macro definition. It retrieves the list of rules.
The compiler then attempts to match the token stream against each rule. It starts with the first rule. It checks if the delimiters match. If they do, it tries to bind the tokens to the fragment specifiers. If the tokens fit, the rule succeeds. The compiler expands the macro using the template and the bound variables.
If the first rule fails, the compiler tries the second rule. It continues until it finds a match or runs out of rules. If no rule matches, the compiler emits "no rules expected the token". It reports the token that caused the failure. This is usually the first token that didn't fit the pattern.
Macros are contracts. Read the signature before you call. The pattern in the macro definition is the contract. Your invocation must honor it.
Realistic example: multi-rule macros
Real-world macros often have multiple rules to support different usage patterns. A logging macro might accept a simple message, or a message with a level. A configuration macro might accept a single key-value pair or a list of pairs.
macro_rules! config {
// Rule 1: Single key-value pair.
// Expects `ident = expr` inside parentheses.
($key:ident = $value:expr) => {
println!("Setting {} to {}", stringify!($key), $value);
};
// Rule 2: Multiple key-value pairs.
// Expects a comma-separated list of `ident = expr`.
($($key:ident = $value:expr),*) => {
$(
config!($key = $value);
)*
};
}
fn main() {
// Matches Rule 1: single pair.
config!(port = 8080);
// Matches Rule 2: multiple pairs.
config!(port = 8080, host = "localhost");
// Fails: missing equals sign.
// The compiler matches `port` to `$key`.
// It expects `=`, but finds `8080`.
// config!(port 8080);
// Error: no rules expected the token `8080`
// Fails: wrong delimiter.
// The rules expect `()`, but the call uses `{}`.
// config! { port = 8080 };
// Error: no rules expected the token `{`
}
When a macro has multiple rules, the compiler tries them in order. If your call matches the structure of a rule but fails on a specific token, the error points to that token. If your call doesn't match any rule structure, the error points to the first token that prevented a match.
The error points to the token that broke the pattern. Look there first. Fix the token or the surrounding syntax to align with the rule.
Pitfalls and debugging
Several common pitfalls trigger this error. Understanding them helps you debug faster.
Delimiters mismatch. The macro definition uses (), but the call uses {}. This is a frequent typo. The compiler rejects the call immediately because the delimiters don't match. Fix the delimiters in the call to match the definition.
Missing separators. Patterns often require separators like commas, equals signs, or keywords. If the pattern is $a:expr, $b:expr, the call must include the comma. macro!(1 2) fails because the compiler matches 1 to $a, expects ,, and finds 2. The error points to 2. Add the missing separator.
Wrong fragment specifier. The pattern expects :ident, but the call provides a string literal or a number. Identifiers are names like foo or bar. They are not values. If you need to pass a value, use :expr. If you need to pass a name, use :ident. Adjust the fragment specifier in the macro definition or change the token in the call.
Repetition syntax errors. Macros use $(...)* or $(...)+ for repetitions. The repetition must match the pattern inside. If the pattern requires a comma between items, the call must include commas. macro!(a b c) fails if the pattern is $($item:ident),*. The repetition expects commas. Add the commas.
Nested macro failures. If a macro calls another macro internally, and the inner call is malformed, the error points to the inner call. The outer macro expands, generates the inner call, and the inner macro rejects it. Check the generated code if you suspect a nested issue.
Convention aside: macro names end in !. This is a community signal. It tells the reader that the item is a macro and requires pattern matching. When you see foo!, expect a pattern. When you see foo, expect a function. Respect the signal.
Trust the pattern matcher. If it rejects you, your tokens don't fit the shape.
Decision: how to fix the mismatch
When you hit this error, you have a mismatch between the call and the definition. Choose the fix based on which side is wrong.
Update the invocation when the macro definition is correct and you used the wrong delimiters, missing separators, or incorrect token types. Fix the call to match the existing pattern.
Add a new rule to the macro when your usage is intentional and the macro is too restrictive. Define a new pattern that accepts your token sequence. Place the new rule in the macro body.
Change the fragment specifier when the macro is rejecting valid tokens you need. Replace :ident with :expr if you need to pass values. Replace :expr with :tt if you need to accept arbitrary token trees. Be careful with :tt as it matches anything and can hide errors.
Replace the macro with a function when the macro is just forwarding arguments without generating new syntax. Macros add complexity. If you don't need text generation or repetition, use a function. Functions have simpler error messages and better IDE support.
Macros are contracts. Read the signature before you call.