1. Symptoms
When Rust’s macro expansion engine encounters a macro invocation that does not match the macro definition’s pattern, the compiler emits error E0666. This error manifests with a clear diagnostic message indicating the specific mismatch between what the macro expected and what was provided in the invocation.
The compiler output typically includes the macro name, the pattern that failed to match, and the tokens that were actually supplied. The error message structure varies slightly depending on the complexity of the mismatch, but the core diagnostic remains consistent across Rust versions.
Shell output examples:
error[E0666]: malformed macro invocation
--> src/main.rs:5:1
|
5 | assert_eq!(a, b, "message");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `expr, expr`
|
help: macro `assert_eq` from crate `std` defined here
--> /rust/std/src/macros.rs:1500:1
|
| macro_rules! assert_eq {
| ($left:expr, $right:expr $(,)?) => { ... };
| ($left:expr, $right:expr, $msg:expr $(,)?) => { ... };
| }
Another common manifestation occurs when calling custom macros with named capture groups:
error[E0666]: malformed macro invocation
--> src/lib.rs:12:8
|
12 | my_macro!(field = "value");
| ^^^^^^^^^^^^^^^^^^ expected `ident:expr`
|
help: macro `my_macro` defined here
--> src/lib.rs:3:5
|
3 | macro_rules! my_macro {
| ($field:ident = $value:expr) => { ... };
| }
The error location points directly to the problematic macro invocation, and when the macro definition is available (such as locally defined macros), the compiler provides helpful context by showing the expected pattern structure.
2. Root Cause
Error E0666 originates from Rust’s macro pattern matching system, which operates through a declarative matching algorithm defined in the Rust Reference. When the compiler expands a macro invocation using macro_rules!, it attempts to match the provided tokens against each arm’s pattern in order of declaration. If no pattern successfully matches the invocation tokens, the compiler cannot proceed with expansion and reports E0666.
The fundamental cause typically falls into one of several categories. First, argument count mismatches occur when invoking a macro with a different number of arguments than any pattern arm expects. For instance, a macro defined to accept two expressions might be invoked with three arguments, creating a structural mismatch that prevents pattern matching.
Second, type mismatches represent a common root cause. Each pattern fragment in a macro_rules! definition expects a specific token type: expr for expressions, ident for identifiers, ty for types, pat for patterns, and so forth. When a provided argument does not match the expected fragment type, the pattern fails to match. This frequently happens when attempting to pass a literal where an identifier is expected, or vice versa.
Third, structural mismatches arise from delimiter inconsistencies. Macro invocations require consistent matching delimitersβparentheses, brackets, or braces must correspond to the pattern’s expected delimiters. Additionally, the presence or absence of separators like commas can cause pattern matching failures when the pattern expects specific separator arrangements.
Fourth, optional token mismatches occur when pattern repetition quantifiers (*, +, ?) fail to capture the correct number of elements. A pattern requiring at least one element (using +) will not match an invocation providing zero elements, and conversely, a pattern accepting zero or more elements (using *) may fail if the invocation provides tokens that cannot be parsed according to the fragment specifier.
The underlying technical mechanism involves Rust’s incremental parsing approach. The compiler attempts to parse the invocation tokens against each pattern arm sequentially, using a longest-match algorithm. Pattern matching considers not just token types but also token precedence and grouping, meaning that precedence differences in expressions can cause seemingly valid invocations to fail matching.
3. Step-by-Step Fix
Resolving E0666 requires identifying the specific mismatch between your macro invocation and its definition. Follow these systematic steps to diagnose and fix the issue.
Step 1: Examine the Macro Definition
First, locate and read the macro’s definition carefully. Identify all pattern arms and their expected fragment types:
// Original macro definition with clear patterns
macro_rules! create_builder {
// Single field pattern
($field:ident: $type:ty) => {
struct Builder {
$field: $type,
}
};
// Multiple fields pattern
($($field:ident: $type:ty),*) => {
struct Builder {
$($field: $type,)*
}
};
}
Step 2: Compare Invocation Against Expected Patterns
Analyze your invocation against each pattern arm. Count arguments, verify fragment types, and check delimiter consistency:
Before:
// Incorrect: missing second argument for two-parameter pattern
create_builder!(name);
// Incorrect: wrong delimiter (brackets instead of parentheses)
create_builder![name: String];
After:
// Correct: matches first pattern arm
create_builder!(name: String);
// Correct: matches second pattern arm with multiple fields
create_builder!(name: String, age: u32);
Step 3: Verify Fragment Type Compatibility
Ensure each argument matches the expected fragment specifier:
Before:
// Incorrect: passing a literal where ident is expected
let result = my_macro!("literal_string", 42);
// Incorrect: passing type where expression is expected
let result = my_macro!(i32, u64);
After:
// Correct: passing identifiers where ident is expected
let field_name = "data";
let result = my_macro!(field_name, 42);
// Correct: passing expressions where expr is expected
let result = my_macro!(some_function(), another_function());
Step 4: Handle Repetition Patterns Correctly
When using repetition patterns, ensure proper repetition counts and separators:
Before:
// Incorrect: pattern with + requires at least one item
// my_macro!([]) would fail - no items provided
// Incorrect: missing separator in repetition
my_macro!(a: i32 b: i32 c: i32);
After:
// Correct: repetition with proper comma separators
my_macro!(a: i32, b: i32, c: i32);
// Correct: using semicolon as separator per pattern definition
my_macro!(a: i32; b: i32; c: i32);
Step 5: Add or Modify Pattern Arms as Needed
If no existing pattern matches your use case, add a new pattern arm:
macro_rules! flexible_macro {
// Existing single-argument pattern
($value:expr) => { ... };
// Existing two-argument pattern
($key:expr, $value:expr) => { ... };
// New three-argument pattern
($key:expr, $value:expr, $options:expr) => { ... };
}
4. Verification
After applying fixes, verify that the macro expansion works correctly by compiling your code and running tests.
Compile-Time Verification
Run the Rust compiler on the modified code to confirm E0666 no longer appears:
cargo build 2>&1 | grep -E "(error|warning)"
A successful fix produces no E0666 errors, though other warnings or errors might appear.
Runtime Verification
For macros that generate runtime behavior, ensure the generated code produces expected results:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_macro_expansion_output() {
let result = my_macro!(field: String, count: u32);
assert!(result.is_ok());
}
}
Execute the test suite:
cargo test
Macro Expansion Inspection
Use cargo expand to visualize the actual macro expansion and verify it matches expectations:
cargo install cargo-expand
cargo expand --lib
This tool expands all macros in your crate, showing the generated code before type checking occurs.
Pattern Coverage Testing
Create test cases covering all pattern arms to ensure comprehensive testing:
// Test the macro with various valid invocation patterns
macro_rules! test_all_patterns {
($($name:ident: $value:expr),*) => {
$({
let result = my_macro!($name: $value);
assert!(result.is_ok(), "Failed for pattern: {}: {:?}", stringify!($name), $value);
})*
};
}
5. Common Pitfalls
Several recurring mistakes lead to E0666 errors that deserve careful attention during development.
Pitfall 1: Confusing Identifiers with Expressions
A frequent error involves passing string literals where identifiers are expected. In Rust macros, "field_name" (a string literal) differs fundamentally from field_name (an identifier). The fragment specifier ident matches only actual identifiers, not string contents:
// INCORRECT - string literal cannot match ident fragment
macro_rules! define_field {
($name:ident: $type:ty) => {
struct Struct { $name: $type }
};
}
// This will fail with E0666
define_field!("computed_name": i32);
// CORRECT - use actual identifier
define_field!(computed_name: i32);
Pitfall 2: Misunderstanding Repetition Separators
Repetition patterns require consistent separators between captured elements. Using inconsistent or missing separators causes matching failures:
// Pattern expects comma-separated items
macro_rules! comma_separated {
($($item:expr),+) => { ... };
}
// INCORRECT - missing commas
comma_separated!(a b c);
// CORRECT - commas present
comma_separated!(a, b, c);
// CORRECT - alternative: pattern with space separators
macro_rules! space_separated {
($($item:expr) *) => { ... };
}
Pitfall 3: Fragment Type Collisions
Certain fragment types cannot contain delimiters that would create parsing ambiguity. Passing delimited groups where simple fragments are expected causes E0666:
macro_rules! accept_expr {
($e:expr) => { ... };
}
// INCORRECT - block contains multiple statements, not a single expression
accept_expr!({ let x = 1; let y = 2; });
// CORRECT - single expression in block
accept_expr!({ let x = 1; x });
Pitfall 4: Optional Trailing Separators
Patterns using the $(...)? optional group require the captured items to be present or absent based on the quantifier semantics. Confusing ? (zero or one) with * (zero or more) leads to unexpected matching failures:
// Pattern with optional trailing comma
macro_rules! optional_comma {
($($item:expr),* $(,)?) => { ... };
}
// Both of these match correctly
optional_comma!(a);
optional_comma!(a,);
optional_comma!(a, b, c);
Pitfall 5: Macro Hygiene Interactions
Rust’s macro hygiene system prevents accidental name collisions, but it can cause confusing E0666 errors when macro-expanded code references identifiers that exist in the macro definition scope but not the invocation scope:
macro_rules! hygienic_example {
($ident:ident) => {
let $ident = 42; // 'x' introduced here
};
}
fn main() {
hygienic_example!(x);
println!("{}", x); // x is visible due to hygiene
}
Understanding these pitfalls helps developers recognize and resolve E0666 errors more quickly, reducing debugging time when working with complex macro invocations.
6. Related Errors
Understanding related error codes helps build a comprehensive mental model of Rust’s macro system and its failure modes.
E0658: Attempt to use disallowed metaclass body
This error occurs when attempting to use advanced macro features that require unstable compiler support. While E0666 concerns pattern matching failures, E0658 indicates attempted use of unsupported macro mechanisms:
// E0658: unsupported feature
macro_rules! experimental {
metaitem outer { ... }
}
E0765: Attempt to refer to incomplete definition of a trait
This error arises when a macro expansion references items from an incompletely defined trait. Unlike E0666 which focuses on invocation syntax, E0765 concerns the semantic validity of expanded code:
trait Incomplete {
fn method() -> Self;
const VALUE: i32; // E0765 if used before complete definition
}
E0415: Identifier not found in this scope
When macro expansion generates references to undefined identifiers, E0415 appears. This error often co-occurs with E0666 when the pattern matching failure prevents proper expansion that would have introduced the necessary identifiers:
macro_rules! reference_generator {
($ident:ident) => {
let $ident = unknown_variable; // E0415 if pattern matched
};
}
E0425: Cannot find value x in this scope
Similar to E0415 but for value bindings rather than type or variable names. Macro invocations that fail with E0666 might also reveal E0425 for other macros in the same crate that had previously matched but generated references to undefined values.
E0422: Cannot find type, struct, or enum variant x in this scope
This error indicates macro-expanded code references a type that does not exist in the current scope. Like the other related errors, E0422 often surfaces during macro debugging when pattern matching succeeds but the resulting expansion contains invalid references.
These related errors share a common thread: they all emerge from the complex interaction between Rust’s macro expansion system and the broader type checking pipeline. Understanding their relationships helps developers diagnose macro-related issues more effectively and recognize when multiple errors stem from a single root cause.