Fix E0732: #[derive] on Enum with No Variants

Rust intermediate Linux macOS Windows WebAssembly

1. Symptoms

When attempting to compile Rust code that uses the #[derive] attribute on an enum with zero variants, the compiler produces the E0732 error:

error[E0732]: #[derive] can't be used with enum that has no data
  --> src/main.rs:3:1
   |
3  | #[derive(Debug, Clone)]
   | ^^^^^^^^^^^^^^^^^^^^^^
4  | enum Void {}
   | ---------- has no variants

The error manifests with any trait in the derive macro, whether it’s Debug, Clone, Default, PartialEq, or any other derive-able trait. The compiler will also highlight the enum definition itself as having no variants.

You may encounter this error in several scenarios:

  • Creating a marker enum for type-level programming
  • Defining a never-type variant (similar to ! in Rust)
  • Accidentally creating an empty enum during refactoring
  • Writing placeholder code that was never completed

The error message is explicit: enums with zero variants cannot support the derive mechanism because there is nothing to derive implementations for.

2. Root Cause

The #[derive] attribute in Rust generates implementations of traits based on the structure of types. For enums, the derive macro examines each variant and generates code that handles construction, comparison, formatting, and other operations.

Zero-variant enums (also called “empty enums” or “void enums”) represent a type that can never be instantiated. They have no constructors, no possible values, and therefore no actual data to process. When the compiler attempts to generate derive implementations, it finds nothing to work with.

The fundamental problem is logical: if a type can never have a value, then:

  • Clone has nothing to clone
  • Debug has nothing to format
  • Default has no value to return
  • PartialEq has no comparisons to perform

This isn’t a limitation of the compilerβ€”it’s a semantic impossibility. The derive macros are correctly rejecting the request because the derived code would never be usable.

Zero-variant enums do exist in Rust for specific purposes (such as marker types or compile-time impossible states), but they cannot use #[derive] for trait implementations.

3. Step-by-Step Fix

There are several approaches to resolve E0732 depending on your use case.

Option 1: Remove the #[derive] Attribute

If the enum genuinely has no variants and no derive macro is needed, simply remove the #[derive] attribute:

Before:

#[derive(Debug, Clone, PartialEq)]
enum Void {}

After:

enum Void {}

The enum still exists and can be used as a marker type, but without deriving any traits.

Option 2: Convert to a Struct with Private Fields

If you need the type to exist but cannot be instantiated, consider a zero-variant struct with a private field instead. However, this requires manual trait implementations:

Before:

#[derive(Debug)]
enum Never {}

impl Never {
    fn new() -> Option<Never> {
        None
    }
}

After:

enum Never {}

impl Never {
    fn new() -> Option<Never> {
        None
    }
}

Option 3: Use a Phantom Marker Type

For type-level programming, use a marker struct instead of an empty enum:

Before:

#[derive(Clone, Copy)]
enum MarkerType {}

struct PhantomMarker;
impl PhantomMarker {
    // marker type methods
}

After:

// Marker struct with no fields - can still derive traits
#[derive(Clone, Copy, Default)]
struct PhantomMarker(u8);

impl PhantomMarker {
    const fn new() -> Self {
        PhantomMarker(0)
    }
}

Option 4: Manually Implement Required Traits

If you must keep the zero-variant enum but need specific trait implementations, implement them manually:

Before:

#[derive(Debug)]
enum Impossible {}

impl std::fmt::Debug for Impossible {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Impossible")
    }
}

After:

// Manual implementation for zero-variant enum
enum Impossible {}

impl std::fmt::Debug for Impossible {
    fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        Ok(())
    }
}

Note that the self parameter is never used since no instances can exist. The compiler allows this pattern.

Option 5: Convert to a Zero-Variant Tuple Struct

Before:

#[derive(Debug)]
enum EmptyEnum {}

After:

// Zero-variant tuple struct - still cannot be instantiated
struct EmptyEnum();

This preserves the type name while avoiding the enum-specific derive restriction.

4. Verification

After applying the fix, verify the code compiles correctly:

cargo build

For the basic fix, verify there are no warnings or errors:

   Compiling my_project v0.1.0 (file:///path/to/project)
    Finished dev [unoptimized + debuginfo] target(s) in 0.32s

Test that the type behaves as expected in your codebase:

// Verify the enum works in your context
fn main() {
    // If using as a marker type
    let _marker: MarkerType = unsafe { std::mem::transmute(0u8) };
    
    println!("Zero-variant enum compiled successfully");
}

Run your test suite to ensure the change doesn’t break existing functionality:

cargo test

If you have a workspace with multiple crates, test each affected crate individually:

cargo test --all

5. Common Pitfalls

Assuming zero-variant enums are useful as-is: Many developers encounter E0732 when trying to use zero-variant enums as placeholder types. Remember that these types can never be constructed, so they have limited direct utility beyond compile-time type constraints.

Forgetting to check all derive occurrences: If the enum appears in multiple files or modules, ensure you remove #[derive] from every location:

// main.rs
#[derive(Debug)]  // Error here
enum Void {}

mod inner {
    #[derive(Clone)]  // Error here too
    enum Void {}
}

Confusing zero-variant enums with unit variants: A unit variant (like enum E { A }) has one variant and can be derived. Zero variants (like enum E {}) cannot be derived:

// This works - one unit variant
#[derive(Debug, Clone)]
enum Working {
    UnitVariant,
}

// This fails - zero variants
#[derive(Debug)]
enum Broken {}

Attempting derive on uninhabited types for pattern matching: Even when using zero-variant enums for exhaustiveness checking in match expressions, you cannot derive traits on them:

fn process(value: SomeEnum) -> bool {
    match value {
        // This works without derive on the never type itself
    }
}

Not understanding the intent of zero-variant enums: These exist primarily for compile-time guarantees that a code path is unreachable. If you need runtime functionality, the type design may need reconsideration.

E0080: “evaluation of constant value failed” - Can occur if a zero-variant enum is used in constant evaluation contexts where the compiler attempts to create a value.

E0433: “failed to resolve” - May appear if derive macros for zero-variant enums attempt to reference code that doesn’t exist due to the empty enum having no variants.

E0369: “binary operation cannot be applied” - Sometimes occurs when attempting operations on zero-variant enum values in generic contexts where trait bounds are satisfied by the enum but no actual values exist.

E0507: “cannot move out of” - Can appear if code tries to move a zero-variant enum value, though in practice this rarely occurs since such values can never exist.

E0599: “no method named” - May occur in generic code where a trait bound includes the zero-variant enum but the derive implementation was never generated.

For zero-variant enums specifically, the most common path to resolution is either removing the derive attribute or redesigning the type to have at least one variant. The compiler’s error E0732 correctly identifies that the derive operation is fundamentally impossible for this type structure.