Types
Built-in types
Section titled “Built-in types”These types come from the language itself, so they need no import.
Prelude types
Section titled “Prelude types”Each of these is declared in the prelude.
| Type | Prelude section |
|---|---|
int int8 int16 int32 int64 | Numerics |
uint uint8 uint16 uint32 uint64 uintptr | Numerics |
byte rune | Numerics |
float32 float64 complex64 complex128 | Numerics |
bool | Booleans |
string | Strings |
Option<T> | Option |
Result<T, E> | Result |
Partial<T, E> | Partial |
Slice<T> | Slice |
Array<T, N> | Array |
Map<K, V> | Map |
Ref<T> | Ref |
Never | Never |
Tuples hold 2 to 5 values, which may have different types. Access elements by position.
let pair = (42, "hello")let first = pair.042let second = pair.1"hello"
let triple = (1, true, "three")let (a, b, c) = triplea is 1, b is true, c is "three"For more than 5 elements, use a struct with named fields.
The implicit return type of functions that return no value. Written as ().
fn greet(name: string) {implies -> () fmt.Println(f"hello, {name}")}Type annotations
Section titled “Type annotations”A type annotation names a type the compiler cannot work out on its own. Write it after : on a binding, parameter, or field, and after -> for a return type.
let empty = []error: element type cannot be inferredlet empty: Slice<int> = []annotation decides element typeMost types are inferred from the value, so annotations are the exception.
Type parameters
Section titled “Type parameters”A type parameter stands for a type supplied later, when the declaration is used. Functions, methods, structs, enums, and aliases all take type parameters, written in angle brackets after the name.
fn first<T>(xs: Slice<T>) -> Option<T>struct Pair<A, B> { left: A, right: B }enum Tree<T> { Leaf(T), Node(Ref<Tree<T>>, Ref<Tree<T>>) }Supply them at the point of use, or leave them out where the context decides:
let scores = Map.new<string, int>()suppliedlet names = ["Alice", "Bob"]inferred as Slice<string>Bounds
Section titled “Bounds”A type parameter can carry a bound T: Constraint that constrains which types may instantiate it. Any interface serves as a constraint, and the prelude supplies two: Comparable and Ordered.
interface Display { fn to_string() -> string}
T must satisfy Displayfn print_value<T: Display>(value: T) { fmt.Println(value.to_string())}To give a type parameter multiple bounds:
fn render<T: Display + Shape>(value: T) -> stringT must satisfy both
Display and ShapeTo give type parameters individual bounds:
fn label<T: Display, U: Ordered>(value: T, rank: U) -> stringT must satisfy Display
U must satisfy OrderedType aliases
Section titled “Type aliases”To alias a type:
type UserId = inttype Handler = fn(Request) -> Responsetype StringMap<V> = Map<string, V>Type aliases are transparent, i.e. alternative names for the same type.
type UserId = int
let id: UserId = 42let n: int = idUserId is just intFor distinct types, use a tuple struct:
struct UserId(int)struct OrderId(int)
let user = UserId(1)let order = OrderId(2)let n: int = usererror: expected int, found UserIdType conversion
Section titled “Type conversion”Types never convert implicitly, not even between numeric types. The as operator converts between types explicitly.
let a: int = 1let b: int64 = 2let c = a + berror: cannot add int and int64let c = a + (b as int)explicit conversionNumeric conversions:
let x: int = 42let y = x as float64let z = x as int8Conversions of string to bytes or runes:
let s = "hello"let bytes = s as Slice<byte>let runes = s as Slice<rune>let back = bytes as stringConversions between incompatible types are disallowed.
let b = truelet n = b as interror: cannot convert bool to int