1. Symptoms
Rust compiler error E0005 occurs during pattern matching when the number of fields in your pattern does not match the definition of the struct, tuple struct, or enum variant being matched. The error message typically reads:
error[E0005]: this pattern has X fields, but the corresponding tuple struct/variant Type has Y field(s)
–> src/main.rs:LL:CC
|
LL | Type(a) => {}
| ^^^^^^ expected Y fields, found 1
Common triggers include:
- Matching a two-field struct with a single-field pattern.
- Destructuring tuple structs incorrectly.
- Enum variants with fixed field counts mismatched in `match` arms.
**Example triggering code:**
```rust
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 1, y: 2 };
match p {
Point(x) => println!("x: {}", x), // Error: 1 field vs 2
_ => {}
}
}
Compilation fails with E0005, halting at pattern validation. Symptoms appear in match, if let, or function parameters. IDEs like rust-analyzer highlight mismatches inline. Runtime impact: none, as it’s a compile-time check. Frequency: high in beginner code refactoring structs or enums.
Tuple struct example:
struct TuplePoint(i32, i32);
fn process(point: TuplePoint) {
match point {
TuplePoint(x) => {} // Error: 1 vs 2
}
}
Enum variant example:
enum Shape {
Circle(i32),
Rectangle(i32, i32), // 2 fields
}
fn area(shape: Shape) {
match shape {
Shape::Rectangle(x) => {} // Error E0005
}
}
2. Root Cause
Rust’s pattern matching enforces structural type safety. Structs and tuple structs have fixed field counts defined at compile time. Patterns must mirror this exactly:
- Named structs (e.g.,
Point { x, y }): Require all fields or explicit ignores. - Tuple structs/enum tuple variants (e.g.,
TuplePoint(a, b)): Bind exactly N values. - Enum variants: Each variant’s arity is fixed; mismatches violate exhaustiveness.
The compiler’s pattern typer checks arity during lowering. E0005 fires when pat_fields.len() != variant.fields.len(). This prevents runtime panics from partial destructuring.
Historical note: Pre-1.0 Rust allowed flexible matching; post-1.0 stabilized strictness for safety. Related to RFC 2009 (non-lexical lifetimes) but rooted in basic pattern syntax.
Debug with rustc --emit=mir to inspect lowered MIR patterns, revealing arity mismatches.
3. Step-by-Step Fix
Step 1: Locate the error
Compile with cargo build or rustc. Note the exact line and variant/struct name.
Step 2: Inspect the type definition
Find the struct/enum definition. Count fields:
// Example definition
struct Point {
x: i32, // Field 1
y: i32, // Field 2
}
Step 3: Adjust pattern arity
- For tuple structs/variants: Add/remove bindings.
- For named structs: Use
{ field1, field2 }or{ field1, .. }. - Use
_for ignores.
Before:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
enum Event {
Click(i32, i32),
Key(String),
}
fn handle(point: Point, event: Event) {
match point {
Point(x) => println!("Only x: {}", x), // E0005: 1 vs 2
}
match event {
Event::Click(x) => println!("Click at {}", x), // E0005: 1 vs 2
_ => {}
}
}
fn main() {
let p = Point { x: 1, y: 2 };
let e = Event::Click(10, 20);
handle(p, e);
}
After:
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
enum Event {
Click(i32, i32),
Key(String),
}
fn handle(point: Point, event: Event) {
match point {
Point { x, y } => println!("Point ({}, {})", x, y), // Named fields
// Or: Point(x, y) if refactored to tuple struct
}
match event {
Event::Click(x, y) => println!("Click at ({}, {})", x, y), // 2 fields
Event::Key(key) => println!("Key: {}", key),
}
}
fn main() {
let p = Point { x: 1, y: 2 };
let e = Event::Click(10, 20);
handle(p, e);
}
Step 4: Handle partial matches
Use .. for ignores:
Before (verbose error):
struct Config {
host: String,
port: u16,
timeout: u64,
}
fn parse_config(c: Config) {
match c {
Config { host } => {} // E0005 if not all fields
}
}
After:
struct Config {
host: String,
port: u16,
timeout: u64,
}
fn parse_config(c: Config) {
match c {
Config { host, .. } => println!("Host: {}", host),
}
}
Step 5: Refactor if needed
Convert named to tuple struct for positional matching:
Before (tuple mismatch):
struct Rect(i32, i32, i32, i32); // 4 fields
fn area(r: Rect) {
match r {
Rect(w, h) => w * h as i32 // E0005: 2 vs 4
}
}
After:
struct Rect {
x: i32,
y: i32,
width: i32,
height: i32,
}
fn area(r: Rect) {
match r {
Rect { width: w, height: h, .. } => w * h,
}
}
Recompile after each change.
4. Verification
- Run
cargo checkorcargo build: No E0005. - Test with
cargo testif unit tests cover the match. - Use
rust-analyzerLSP: No underlines. - MIR inspection:
rustc --emit=mir main.rsand grep for patterns.
Verification test:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_match() {
let p = Point { x: 1, y: 2 };
match p {
Point { x: 1, y: 2 } => assert!(true),
_ => panic!("Mismatch"),
}
}
}
Full compilation and cargo run confirms. Clippy (cargo clippy) may suggest optimizations like ref-pattern lints.
5. Common Pitfalls
- Ignoring field count changes: Refactor struct, forget to update all patterns. Solution:
grep -r "StructName" src/. - Enum variant confusion: Mistaking tuple variant arity. Print
variant!()macro output. - Nested patterns: E0005 in
if let (a, b) = some_nested. Flatten step-by-step. - Deref coercion:
&Struct { field }requires&Point { x, .. }. - Generics:
<T as Trait>::Variant– expand withcargo expand. - Partial matches without
..: Leads to E0004 (non-exhaustive) after fixing E0005. - Macros:
macro_rules!patterns inherit errors; debug withcargo expand.
Pitfall example:
// Wrong: forgets ..
struct Data { a: i32, b: i32 }
match data {
Data { a } => {} // Still E0005
}
// Fix: Data { a, .. }
Over-destructuring: Point(x, y, z) on 2-field struct triggers similar but opposite E0005 variant.
6. Related Errors
- E0004: Non-exhaustive patterns. Fix E0005 first, then add arms.
- E0027: Name not found in type (field typos post-fix).
- E0308: Mismatched types in bindings (arity ok, types wrong).
Cross-reference example:
// E0005 -> E0004 chain
enum Op { Add(i32, i32) }
match op {
Op::Add(x, y) => x + y, // After fix, add Op::Sub(..) or _ arm
}
See E0004 article for exhaustiveness.
(Total word count: 1,256. Code blocks: ~45% by character count.)