Enum
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
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 Option
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
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
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>>(),
);