Null Safety
Saule enforces null safety at compile time. A type is only nullable if declared with ?.
local name: string? = nil -- ok, nullablelocal name: string = nil -- ERROR, string is never nilSafe Access
Section titled “Safe Access”Use ?. to access a member that may be nil. Returns nil instead of crashing:
class Player name: string = "" fn init(name: string) self.name = name endend
class Repo fn findById(id: integer) -> Player? if id == 1 then return Player("Ada") end return nil endend
local repo: Repo = Repo()local id: integer = 2
local player: Player? = repo.findById(id)local name: string? = player?.name -- nil if `player` was nilprintln(name ?? "<none>") -- <none>For lengths of strings, bytes and tables, use #:
local greeting: string? = nillocal len: integer = #(greeting ?? "") -- 0 when nilSafe Index
Section titled “Safe Index”?[...] is the index half of the same idea — nil when the receiver is nil, the element otherwise:
local rows: table<integer>? = nilprintln(rows?[1] ?? 0) -- 0, no fault on the nil receiverThe index expression is not evaluated at all on the nil path, the same rule ?. follows for a call’s arguments, and the two chain together:
class Grid rows: table<table<integer>>? = nilend
local grid: Grid = Grid()println(grid.rows?[1]?[2] ?? -1) -- -1, nothing to index
grid.rows = {{7, 8}}println(grid.rows?[1]?[2] ?? -1) -- 8Null Coalescing
Section titled “Null Coalescing”Use ?? to provide a fallback when a value is nil:
local name: string? = nillocal display: string = name ?? "Unknown"println(display) -- UnknownForce Unwrap
Section titled “Force Unwrap”Use ! to assert a value is not nil. Crashes at runtime if it is:
local name: string? = "Arthur"local forced: string = name!println(forced) -- ArthurCombining Operators
Section titled “Combining Operators”class Player name: string = "" fn init(name: string) self.name = name end fn getName() -> string return self.name endend
fn getPlayers() -> table<Player?> return {Player("Ada"), nil}end
local players: table<Player?> = getPlayers()
for player: Player? in players do local name: string = player?.getName() ?? "Unknown"
println(name) -- Ada, then Unknownend