Enums / Tagged Unions
Enums in Mux are tagged unions (also called algebraic data types or sum types) that can hold different variants with associated data.
Basic Enum Definition
Key Points:
- Each variant is a case of the enum
- Variants can carry associated data (or none)
- Every payload field is named, like a function parameter:
Circle(float radius)
Creating Enum Instances
A variant is always reached through the enum name, and parentheses mean
arguments. A variant with no payload takes none; writing () anyway is an
error, not an accepted alias.
Three things are rejected, each with a suggestion:
A variant is also not reachable through a value of the enum - myShape.Circle
is an error suggesting match instead.
Pattern Matching with Enums
Use match to handle different enum variants:
Ignoring Associated Data
Use _ to ignore data you don't need:
Pattern Matching with Guards
Add conditional logic with guards:
Arms are tried in order, so the guarded arms have to come before the bare one -
Some(v) with no guard matches everything the guarded arms would have.
Generic Enums
An enum can be generic over type parameters. Each instantiation is
monomorphized - Box<int> gets its own layout holding a real int, not a
boxed pointer - so there is no cost to reaching for one.
The type argument has to be concrete at the point of use: Box<int>.Full(42),
not Box.Full(42).
optional and result are built in and are not user enums - you cannot
declare your own type named optional or result, and you construct them with
some, none, ok and err rather than through an enum name. See
optional Values below.
Comparing Enums
Enums compare structurally with == and !=: same variant, and equal payloads.
The comparison recurses, so nested and recursive enums compare all the way
down. This is the same comparison that lets an enum be a map key or a set
member.
optional and result compare the same way. There is no ordering for enums -
< is not defined on them.
Enums Have No to_string()
Color.Red.to_string() is an error, and deliberately so: only you know whether
HTTPCode.Ok should render as "Ok" or as "200". Write the conversion as a
function and call it:
Exhaustiveness checking makes this safe to extend: adding a variant turns every such function into a compile error listing what is missing, rather than a silently wrong default.
Common Enum Patterns
optional Values
result Types for Error Handling
See Error Handling for more details on result and optional.
State Machines
Nested Enums
Enums in Collections
Exhaustiveness Checking
Mux enforces exhaustive pattern matching - all variants must be covered:
Best Practices
- Use enums for mutually exclusive states - Better than multiple booleans
- Match exhaustively - Don't overuse wildcard patterns
- Use guards for additional logic - Cleaner than nested if statements
- Ignore unused data with
_- Makes intent explicit - Prefer result over exceptions - Explicit error handling
- Prefer optional over null - No null pointer errors
- Use generic enums for reusable patterns - Option<T>, result<T, E>
See Also
- Error Handling - result and optional types
- Control Flow - Pattern matching with match
- Generics - Generic enums
- Collections - Enums in lists and maps