Guide
Options
There is no null. Absence is a type, and the compiler will not let you forget it.
There is no null in this language and no undefined. An option is the only way to say a value might not be there, and the compiler will not let you forget which you are holding.
some and none
T? is an option. A bare value does not flow into an option position: returning 1 where f32? was declared is an error, and some(1) is what was meant.
// An option is how a value says it might not be there. There is no null.
fn firstOrNone(present: bool) -> f32? {
if present {
return some(1)
} else {
return none
}
}
// `if let` is how an option is read. There is no way to reach the value without answering the
// question first, which is the whole point.
fn readOr(value: f32?, fallback: f32) -> f32 {
if let found = value {
return found
} else {
return fallback
}
}
data Door {
open: bool = false
angle: f32 = 0
}
fn find(present: bool) -> Door? {
if present {
return some(Door { open: true, angle: 90deg })
} else {
return none
}
}
// `?.` reaches through an option and the result is still an option, which is deliberate: an
// operation that may not have run must not be treated as though it did.
fn angleOf(present: bool) -> f32? {
let door = find(present)
return door?.angle
}
Why the bare value is refused
Accepting it would reinvent implicit null in the other direction. A reader could no longer tell an absence that was decided from one that was never filled in, which is the whole thing an option exists to distinguish.
// Deliberately refused.
//
// A bare value does not flow into an option position. `return 1` from a function returning `f32?`
// is an error, and `return some(1)` is what was meant. A language that accepted the first has
// reinvented implicit null in the other direction: a reader can no longer tell an absence that
// was decided from one that was never filled in.
fn find(present: bool) -> f32? {
if present {
return 1
} else {
return none
}
}
Reaching through one
`?.` reaches through an option and the result is still an option. That is deliberate: an operation that may not have run must not be treated as though it did.
// An option is how a value says it might not be there. There is no null.
fn firstOrNone(present: bool) -> f32? {
if present {
return some(1)
} else {
return none
}
}
// `if let` is how an option is read. There is no way to reach the value without answering the
// question first, which is the whole point.
fn readOr(value: f32?, fallback: f32) -> f32 {
if let found = value {
return found
} else {
return fallback
}
}
data Door {
open: bool = false
angle: f32 = 0
}
fn find(present: bool) -> Door? {
if present {
return some(Door { open: true, angle: 90deg })
} else {
return none
}
}
// `?.` reaches through an option and the result is still an option, which is deliberate: an
// operation that may not have run must not be treated as though it did.
fn angleOf(present: bool) -> f32? {
let door = find(present)
return door?.angle
}