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-8Zero 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}...) // spreadClosure
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. orderInitialization 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).XMethods 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 closedOnly 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.Stderrtime
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=. -benchmemModules
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, IDpanic / 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.