Enum

From Rust Community Wiki
Jump to navigation Jump to search

An enum (from "enumeration") is a type that can be in one of several possible states, which are called variants. If it has exactly one variant, it is semantically similar to a struct. If it has no variants, it is semantically similar to the never type.

Enums are called sum types or tagged unions in some other languages. Although languages such as C, C# or Java have enums as well, Rust's enums are much more powerful.

Example[edit | edit source]

enum Vegetable {
    Carrot,
    Tomato,
    Onion,
}

This is an enum with three variants, Carrot, Tomato and Onion. To allow converting an enum to a number, its representation can be set to an integer type. Furthermore, the value for each variant can be specified:

#[repr(u32)]
enum Vegetable {
    Carrot = 1,
    Tomato = 2,
    Onion = 4,
}

Like structs, enum variants can contain values. For example, this is how the OptionThis links to official Rust documentation type is defined:

enum Option<T> {
    Some(T),
    None,
}

An Option is either Some and contains a value, or it is None. Note that fields of enum variants are always public.

Using enums[edit | edit source]

Variants of enums are specified with the scope operator (::). They can also be imported:

use Vegetable::Onion;

let carrot = Vegetable::Carrot;
let tomato = Vegetable::Tomato;
let onion  = Onion;

To test if an enum value is a certain variant, we need pattern matching:

fn eat(v: Vegetable) {
    match v {
        Vegetable::Carrot => println!("delicious!"),
        Vegetable::Tomato => println!("yuck!"),
        Vegetable::Onion  => panic!(),
    }
}

Layout optimizations[edit | edit source]

Because Rust doesn't have a null pointer, values that might not be present must be wrapped in an Option. In many cases, this does not introduce any memory overhead, because Rust cleverly optimizes its layout.

For example, the Vegetable type from above only has 3 possible values, and Option<Vegetable> has 4. The compiler recognizes this, so it fits an Option<Vegetable> in a single byte. The same is true for Result<Option<Vegetable>, ()> for example, which has 5 possible values, so it still fits in a single byte.

Perhaps the most useful application of this optimization are pointers and references within an Option. Since Rust doesn't have null, they can never be zero. This allows the compiler to represent the None variant as zero, so an Option<&T> or Option<Box<T>> still has the same size as &T or Box<T>. This is sometimes called a niche optimization, since pointers have a niche (an illegal value) that is filled with an enum variant.

A special case of this optimization is an enum with no variants or the never type. Since a value of such a type can't be constructed, it can always be optimized away:

enum Infallible {}

assert_eq!(
    size_of::<u64>(),
    size_of::<Result<u64, Infallible>>(),
);