Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

A generic enum in Rust is an algebraic data type (ADT) parameterized over one or more types. By declaring type parameters within angle brackets (<T>) after the enum’s identifier, the enum’s variants can store data of arbitrary types. The concrete types are resolved at compile time, allowing a single enum definition to represent multiple distinct types safely.

Syntax and Declaration

Type parameters are declared at the enum level and can be utilized by any of its variants. Variants are not required to use the type parameters.
enum Container<T, U> {
    Single(T),
    Pair(T, U),
    Empty, // Variants can omit generic parameters entirely
}

Instantiation and Type Inference

When instantiating a generic enum, the Rust compiler infers the concrete types based on the provided values. If a variant that does not utilize the generic parameters (like Empty above) is instantiated, explicit type annotation or the turbofish syntax (::<>) is required because the compiler lacks the context to infer the types.
// Type inferred as Container<i32, f64>
let pair_val = Container::Pair(42, 3.14);

// Explicit type annotation required for variants without generic data
let empty_val: Container<i32, f64> = Container::Empty;

// Alternatively, using turbofish syntax
let another_empty = Container::<String, bool>::Empty;

Monomorphization

Rust implements generics using monomorphization. During compilation, the compiler generates a distinct, concrete copy of the enum for every unique combination of types it is instantiated with. For example, if a program uses both Container<i32, i32> and Container<String, f64>, the compiler produces two entirely separate types in the final binary. This guarantees zero-cost abstraction at runtime, as there is no dynamic dispatch or boxing overhead, though it can increase the compiled binary size.

Trait Bounds

Generic type parameters in enums can be constrained using trait bounds. While bounds can be placed directly on the enum definition, it is idiomatic Rust to leave the enum definition unbounded and apply the trait bounds to the impl blocks. This prevents requiring the trait bound in contexts where the specific behavior is not needed.
enum Message<T> {
    Payload(T),
    None,
}

// Trait bound applied only to the implementation block
impl<T: std::fmt::Display> Message<T> {
    fn print_payload(&self) {
        if let Message::Payload(val) = self {
            println!("{}", val);
        }
    }
}

Structural Examples in the Standard Library

The Rust standard library relies heavily on generic enums for its core types. Structurally, the two most prominent examples are defined as follows:
// Parameterized over a single type T
enum Option<T> {
    Some(T),
    None,
}

// Parameterized over two types: T (success) and E (error)
enum Result<T, E> {
    Ok(T),
    Err(E),
}
These definitions demonstrate how generic parameters allow a single structural layout to be reused across the entire type system.
Master Rust with Deep Grasping Methodology!Learn More