Chapter 3

Functions & Structs

Functions do things. Structs shape data. Both are deliberately plain, because the interesting machinery in Sure lives elsewhere.

Functions

A function declares its parameters and, if it returns a value, its return type after ->. A function with a return type must return on every path — the typechecker verifies this and will tell you when it can't be sure.

func clamp(n: Int, lo: Int, hi: Int) -> Int {
    if n < lo {
        return lo
    }
    if n > hi {
        return hi
    }
    return n
}

func main() -> Int {
    print(clamp(140, 0, 100))
    return 0
}

Functions without a return type just do their work and stop. Argument counts and types are checked at every call. There is no overloading, no default arguments, and no variadics: a function takes what it takes.

Structs

A struct is named data. Fields are declared with their types; a struct literal must supply every field by name. There is no partial initialization, and there is no null to fill the gaps — a value either exists in full or does not exist.

struct Point {
    x: Float,
    y: Float,
    label: String,
}

func main() -> Int {
    let origin = Point { x: 0.0, y: 0.0, label: "origin" }
    print(origin.label)
    print(origin.x)
    return 0
}

Field access uses dots and chains as deep as your data does (segment.a.x). Fields can be assigned through any binding — the binding names the struct; the struct's insides remain its own business:

struct Counter {
    hits: Int,
}

func bump(c: Counter) {
    c.hits = c.hits + 1
}

func main() -> Int {
    let c = Counter { hits: 0 }
    bump(c)
    bump(c)
    print(c.hits)    // 2 — c names the same struct everywhere
    return 0
}

Structs are references

That last example worked because struct values live on the heap and variables hold references to them. Passing a struct to a function passes the reference; no copy is made. The same is true of strings, arrays, maps, and channels. The four basic types — Int, Float, Bool — travel by value.

Heap values are the ones whose lifetimes must end somewhere. Where, exactly, is the subject of chapter 7; for now it is enough to know that when a struct is deleted, everything it owns — its strings, its structs, their strings — is deleted with it, recursively, in declaration order.

One rule about shape. A struct may not contain its own type, directly or through a chain of other structs — deletion is recursive and must terminate. The compiler rejects struct Node { next: Node, } with struct 'Node' contains itself. If you need linked structures, use an array; if you need a tree, a struct may contain an array of its own type, which is permitted because an array can be empty and a recursion can therefore end.