Skip to main content
eLearner.app

Go Course

Cheatsheet

A quick reference — the essential syntax of modern Go (1.21+) on a single page. Use Ctrl/Cmd + P to print it.

Go · Cheatsheet — eLearner.app

Declarations and types

  • Variables

    var x int = 10        // explicit
    var y = 3.14          // inferred
    z := "ciao"           // short, only inside functions
    const Pi = 3.14159    // constant

    := only in function scope. Outside, use var.

  • Basic types

    bool                  // true / false
    int  int8  int16  int32  int64
    uint uint8 ... uintptr
    byte = uint8          // alias
    rune = int32          // Unicode codepoint
    float32 float64
    string                // immutable, UTF-8
  • Zero value

    var i int       // 0
    var s string    // ""
    var b bool      // false
    var p *int      // nil
    var m map[string]int  // nil
    var sl []int    // nil (len 0, cap 0)

    In Go every declared variable has a zero value: no undefined.

  • Conversions

    i := 42
    f := float64(i)       // 42.0
    s := string(rune(65)) // "A"  (rune!)
    // string<->number: use strconv
    n, err := strconv.Atoi("42")
    s := strconv.Itoa(42)

    string(42) does NOT yield "42": it yields the character U+002A. Use strconv.

Flow Control

  • if with init

    if v, err := f(); err != nil {
        return err
    } else {
        use(v)
    }
    // no parentheses around condition
    // brace { on the same line (enforced by gofmt)
  • for (only loop)

    for i := 0; i < 10; i++ { ... }   // classic
    for cond { ... }                  // while
    for { ... }                        // infinite
    for i, v := range slice { ... }
    for k, v := range mappa { ... }   // non-det. order
    for i := range canale { ... }
  • switch

    switch x {
    case 1, 2: ...
    case 3: ...
    default: ...
    }
    // no implicit break (no fallthrough by default)
    switch {                  // like if/else
    case a > b: ...
    case a < b: ...
    }
  • defer

    f, err := os.Open(path)
    if err != nil { return err }
    defer f.Close()
    // executed upon function return,
    // arguments evaluated immediately (LIFO)

Functions

  • Definition

    func somma(a, b int) int { return a + b }
    
    // multiple returns + named results
    func dividi(a, b float64) (q float64, err error) {
        if b == 0 {
            err = errors.New("div per zero")
            return
        }
        q = a / b
        return
    }
  • Variadics

    func sum(nums ...int) int {
        tot := 0
        for _, n := range nums { tot += n }
        return tot
    }
    sum(1, 2, 3)
    sum([]int{1, 2, 3}...)  // spread
  • Closure

    func contatore() func() int {
        n := 0
        return func() int { n++; return n }
    }
    c := contatore()
    c() // 1
    c() // 2

Slices and arrays

  • Slices

    s := []int{1, 2, 3}
    s = append(s, 4)         // always reassign
    len(s)  cap(s)
    s2 := s[1:3]             // shared slice, watch out!
    s3 := make([]int, 0, 16) // len 0, cap 16
    copy(dst, src)

    append can reallocate: the return value MUST be reassigned.

  • Arrays (fixed length)

    var a [3]int            // [0 0 0]
    a := [3]int{1, 2, 3}
    a := [...]int{1, 2, 3}  // compiler counts
    // arrays are values: passing them by value copies everything

Maps

  • Create and use

    m := map[string]int{"a": 1, "b": 2}
    m["c"] = 3
    v, ok := m["x"]   // ok=false if missing
    delete(m, "a")
    len(m)
    for k, v := range m { ... }  // non-det. order
  • Initialization required

    var m map[string]int    // nil
    m["a"] = 1              // PANIC: write to nil map
    m = make(map[string]int)
    m["a"] = 1              // ok

Strings

  • Operations

    s := "Ciao, mondo"
    len(s)                      // bytes, not runes
    strings.ToUpper(s)
    strings.Contains(s, "mondo")
    strings.Split(s, ", ")
    strings.Join(parts, "-")
    strings.HasPrefix(s, "Ciao")
    strings.ReplaceAll(s, "o", "0")
  • Builder for loop

    var b strings.Builder
    for _, w := range parole {
        b.WriteString(w)
        b.WriteByte(' ')
    }
    out := b.String()
    // AVOID s += w in loops: O(n^2)
  • Iterate runes (Unicode)

    for i, r := range "caffè" {
        // i = byte offset, r = rune (int32)
        fmt.Printf("%d %c\n", i, r)
    }
    // len("caffè") == 6 (bytes), not 5

Structs and methods

  • Structs

    type Punto struct {
        X, Y float64
    }
    p := Punto{X: 3, Y: 4}
    p.X = 5
    
    // pointers
    q := &Punto{1, 2}
    q.X        // auto-deref: no (*q).X
  • Methods and receivers

    func (p Punto) Distanza() float64 {
        return math.Hypot(p.X, p.Y)
    }
    
    // pointer receiver: modifications and/or large types
    func (p *Punto) Trasla(dx, dy float64) {
        p.X += dx; p.Y += dy
    }

    Consistency: all methods of a type should have receivers of the same type (value or pointer).

  • Composition (embedding)

    type Animale struct{ Nome string }
    func (a Animale) Saluta() string { return "Ciao " + a.Nome }
    
    type Cane struct {
        Animale          // embed
        Razza string
    }
    c := Cane{Animale{"Rex"}, "Lab"}
    c.Saluta()           // promoted from Animale

Interfaces and errors

  • Interfaces

    type Stringer interface {
        String() string
    }
    // satisfied implicitly: no "implements"
    func (p Punto) String() string {
        return fmt.Sprintf("(%.1f, %.1f)", p.X, p.Y)
    }
  • Type assertion / switch

    if s, ok := v.(string); ok { use(s) }
    
    switch x := v.(type) {
    case int:    use(x)
    case string: use(x)
    default:     fmt.Println("?")
    }
  • Idiomatic errors

    if err != nil {
        return fmt.Errorf("apri %s: %w", path, err)
    }
    // compare sentinels
    if errors.Is(err, io.EOF) { ... }
    // typed cast
    var pe *fs.PathError
    if errors.As(err, &pe) { use(pe.Path) }

    Always %w to wrap. Never duplicate the message of the original error.

Goroutines and channels

  • Goroutines

    go funzione(arg)            // fire-and-forget
    // sync with WaitGroup
    var wg sync.WaitGroup
    wg.Add(1)
    go func() {
        defer wg.Done()
        fai()
    }()
    wg.Wait()
  • Channels

    ch := make(chan int)        // unbuffered
    ch := make(chan int, 4)     // buffered
    ch <- 42                    // send
    v := <-ch                   // receive
    v, ok := <-ch               // ok=false if closed
    close(ch)                   // only by producer
    for v := range ch { ... }   // until closed

    Only the producer closes. Never close a receive-only channel.

  • select

    select {
    case v := <-ch1:    use(v)
    case ch2 <- 42:     // send if possible
    case <-time.After(time.Second):
        return errors.New("timeout")
    case <-ctx.Done():
        return ctx.Err()
    }
  • context

    ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
    defer cancel()
    go work(ctx, ...)
    // inside work:
    select {
    case <-ctx.Done(): return ctx.Err()
    case res := <-out: return res
    }

Essential Stdlib

  • fmt

    fmt.Println("ciao", 42)
    fmt.Printf("%s = %d\n", k, v)
    // verbs: %v default, %+v fields, %#v Go-syntax, %T type, %q quoted, %w wrap
    fmt.Errorf("apri %s: %w", path, err)
  • io / os

    data, err := os.ReadFile(path)
    err = os.WriteFile(path, data, 0644)
    f, err := os.Open(path); defer f.Close()
    io.Copy(dst, src)
    os.Args     os.Stdin     os.Stdout     os.Stderr
  • time

    t := time.Now()
    t.Format("2006-01-02 15:04:05")  // layout magic!
    d := 5 * time.Second
    t.Add(d)   time.Since(t)
    time.Sleep(d)
    // Sleep(5) would sleep 5 nanoseconds
    timer := time.NewTimer(d)
    ticker := time.NewTicker(d); defer ticker.Stop()
  • encoding/json

    type User struct {
        Name  string `json:"name"`
        Email string `json:"email,omitempty"`
        pwd   string  // unexported: ignored
    }
    b, err := json.Marshal(u)
    err = json.Unmarshal(b, &u)   // POINTER
    json.NewEncoder(w).Encode(u)
    json.NewDecoder(r).Decode(&u)

Testing and modules

  • Tests

    // file: xxx_test.go
    func TestSomma(t *testing.T) {
        got := Somma(2, 3)
        if got != 5 {
            t.Fatalf("got %d, want 5", got)
        }
    }
  • Table-driven

    func TestDouble(t *testing.T) {
        cases := []struct {
            name string
            in, want int
        }{
            {"zero", 0, 0},
            {"positivo", 3, 6},
        }
        for _, tc := range cases {
            t.Run(tc.name, func(t *testing.T) {
                if got := Double(tc.in); got != tc.want {
                    t.Errorf("got %d, want %d", got, tc.want)
                }
            })
        }
    }
  • Benchmarks

    func BenchmarkX(b *testing.B) {
        for i := 0; i < b.N; i++ { X() }
    }
    // go test -bench=. -benchmem
  • Modules

    go mod init github.com/me/pkg
    go get example.com/foo@v1.2.3
    go mod tidy
    go mod why example.com/foo
    // import: full path, always

Generics and idioms

  • Generics

    func Map[T, U any](s []T, f func(T) U) []U {
        out := make([]U, len(s))
        for i, v := range s { out[i] = f(v) }
        return out
    }
    // custom constraint
    type Numero interface {
        ~int | ~float64
    }
    func Sum[T Numero](nums []T) T { ... }

    cmp.Ordered (Go 1.21+) for <, <=, ==. comparable for ==.

  • Visibility & naming

    // Exported: capital letter
    type User struct{}
    func New() *User { ... }
    
    // Private: lowercase
    type cache struct{}
    func calc() int { ... }
    
    // acronyms all caps: HTTPClient, URL, ID
  • panic / recover

    // panic ONLY for unrecoverable bugs or init
    if must == nil { panic("must non nil") }
    
    // recover ONLY at the system boundary
    defer func() {
        if r := recover(); r != nil {
            log.Printf("panic: %v", r)
        }
    }()

    In normal code, return an error. Panic is not a substitute for error handling.

eLearner.app · Go Course · cheatsheet generated from lesson content.