Skip to content
Sword

Pointers and optionals

*T is a pointer, and it is never null. Absence lives in ?T:

struct Node {
key i32
next ?*Node
}
func find(head ?*Node, key i32) ?*Node {
mut cur := head
for {
if n := cur {
if n.key == key {
return n
}
cur = n.next
} else {
return nil
}
}
}
func main() int {
mut c := Node{key: 3, next: nil}
mut b := Node{key: 2, next: &c}
mut a := Node{key: 1, next: &b}
hit := find(&a, 3) orelse return 1
return int(hit.key) + 39
}

?*Node still fits in one word — the null pointer is the marker, so the safety costs nothing. Over a non-pointer type, ?T is a value and a flag beside it.

Two ways to get at the value, and no third:

  • if n := opt { ... } runs the block with the unwrapped value bound
  • opt orelse fallback produces the value or the fallback

An error’s name and message are available too, which is what a log line or a test failure wants:

import "std/io"
error NotFound = "there is nothing there"
func risky() !u64 {
return error.NotFound
}
func main() !int {
risky() catch |e| {
try io.Printf("gave up: {} — {}\n", nameof(e), e.Message())
}
return 0
}

orelse also takes a jump, which is the shape you will write most:

p := lookup(key) orelse return error.NotFound

To ask only whether something is there, compare it against nil:

import "std/io"
import "std/os"
func main() !int {
if os.Env("HOME") != nil {
try io.Print("HOME is set\n")
}
return 0
}

That is the only comparison an optional has. Two of them cannot be compared, because that would mean comparing the values inside, and an absent one has none.