01 — Basics

The parts of Go where the mental model differs from C: zero values, integer overflow, multiple return values, slices, append, and explicit conversions. Every example is live — edit it and press Run.

1. Zero values

Every declared variable in Go is initialised to its type's zero value — you never read an indeterminate value the way you can with an uninitialised local in C. Numbers start at 0, strings at "", booleans at false.

package main

import "fmt"

func main() {
    var x int
    var f float64
    var s string
    var b bool
    fmt.Printf("%d %g %q %t\n", x, f, s, b)
}

Output:

0 0 "" false

2. Integer overflow is defined

When arithmetic exceeds a type's range, Go wraps modulo 2ⁿ for both signed and unsigned integers — no panic. This is the two's-complement wraparound you know from C's unsigned types, but Go defines it for signed integers too (where C leaves it undefined). An out-of-range constant, by contrast, is a compile error.

package main

import "fmt"

func main() {
    var x int8 = 127
    x++ // wraps to -128
    var u uint8 = 255
    u++ // wraps to 0
    fmt.Println(x, u)
}

Output:

-128 0

3. Multiple return values

A Go function can return several values directly, named in the signature. In C you'd return one value and use pointer out-parameters for the rest. Here MinMax returns the smallest, the largest, and an ok flag that is false for an empty slice — the value, ok idiom you'll see throughout Go.

package main

import "fmt"

func MinMax(xs []int) (min, max int, ok bool) {
    if len(xs) == 0 {
        return // bare return: 0, 0, false
    }
    min, max, ok = xs[0], xs[0], true
    for _, v := range xs[1:] {
        if v < min {
            min = v
        }
        if v > max {
            max = v
        }
    }
    return
}

func main() {
    lo, hi, ok := MinMax([]int{3, 1, 4, 1, 5, 9, 2, 6})
    fmt.Println(lo, hi, ok)
    fmt.Println(MinMax(nil))
}

Output:

1 9 true
0 0 false

The names (min, max int, ok bool) are return declarations, not inputs: they're pre-declared variables starting at their zero values, and a plain return hands back whatever they hold.

4. Slices and append

A slice is a lightweight window onto a backing array: a header of {pointer, length, capacity}. Taking a sub-slice shares the same storage — no copy — so writing through one view is visible through the other. And append may reallocate, so you must capture its return value (s = append(s, x)).

package main

import "fmt"

func main() {
    s := []int{10, 20, 30, 40}
    mid := s[1:3]  // window over elements 1,2 — same backing array
    mid[0] = 99    // visible in s too
    fmt.Println(s)
    fmt.Println(len(s), cap(s))

    var xs []int        // nil slice: usable as empty
    xs = append(xs, 1)  // append to nil is fine
    xs = append(xs, 2)
    fmt.Println(xs)
}

Output:

[10 99 30 40]
4 4
[1 2]

5. No implicit numeric conversions

Go never converts between numeric types automatically — you write T(x). That makes truncation visible, but the conversion itself is unchecked, just like a C cast: an out-of-range integer keeps its low bits, and a float truncates toward zero.

package main

import "fmt"

func main() {
    var big int32 = 300
    var small int8 = int8(big)   // 300 doesn't fit int8 -> wraps to 44
    var neg int32 = -1
    var u uint8 = uint8(neg)     // -1 -> 255 (low 8 bits)
    var fl float64 = 3.9
    var n int = int(fl)          // truncates toward zero -> 3
    fmt.Println(small, u, n)
}

Output:

44 255 3

Recap

ConceptIn Go
Uninitialised variableImpossible — zero value (0, "", false, nil)
Integer overflowWraps modulo 2ⁿ at runtime; out-of-range constant is a compile error
Several resultsMultiple return values (v, ok)
Dynamic sequence[]T — a {ptr, len, cap} window
Grow a slices = append(s, x) — always reassign
Mix numeric typesExplicit T(x); unchecked like a C cast

Next: 02 — Strings & Runes.