Data types
Sum types
A sum type lists alternatives, each of which may carry a payload:
type Option<T> = None | Some(T)
type Result<T, E> = Ok(T) | Err(E)
type List<T> = Nil | Cons(T, List<T>)
type Tree<T> = Leaf | Node(T, Tree<T>, Tree<T>)
They may be written across several lines with a leading |, which reads better
once there are more than two:
type Shape =
| Circle(Nat)
| Rect(Nat, Nat)
A single-constructor type needs no leading bar:
type Pair<A, B> = MkPair(A, B)
Note that List and Tree above refer to themselves. Recursive types are
ordinary types here, and the compiler handles the heap allocation. Two rules
apply to them: a type may not refer to itself to the left of an arrow (see
Strictly positive types), and a
type whose recursion passes through another generic type — Node(List<Tree>)
— cannot be matched on (see A recursive type nested inside a generic
one).
Take values apart with match.
Records
A record has named fields:
type Point = { x: Nat, y: Nat }
type Span = { start: Nat, end: Nat, file: String }
Construct one with braces and read a field with a dot:
fn make_point(x: Nat, y: Nat) -> Point {
{ x: x, y: y }
}
fn get_x(p: Point) -> Nat {
p.x
}
Named constructors
Since 1.5
Prefixing the braces with the type name says which record you mean, which matters when the expected type is not otherwise obvious:
fn origin() -> Point {
Point { x: 0, y: 0 }
}
Record spread
Since 1.5
Build a record from an existing one, overriding selected fields:
fn shift(p: Point, dx: Nat) -> Point {
{ ...p, x: p.x + dx }
}
The spread comes first, and later fields win. This is a functional update — p
is unchanged.
Type aliases
An alias is a second name for an existing type:
pub type Char = Nat
pub type SourceEntry = (String, String)
Aliases may take type parameters — see Generics.
Choosing between them
Reach for a sum type when a value is one of several shapes, and let exhaustiveness checking make sure every shape is handled. Reach for a record when a value has several independent parts that are all present at once.
A tuple is for when the parts are not worth naming. Note that tuples have no
index access — there is no p.0; you get at the parts by
destructuring, which means every use
site names them anyway. Once that naming starts feeling repetitive, it is a
sign the value wants to be a record.