Corso Go
Cheatsheet
Un riferimento veloce — la sintassi essenziale di Go moderno (1.21+) su una pagina sola. Usa Ctrl/Cmd + P per stamparla.
Go · Cheatsheet — eLearner.app
Dichiarazioni e tipi
Variabili
var x int = 10 // esplicita var y = 3.14 // inferita z := "ciao" // short, solo dentro funzione const Pi = 3.14159 // costante:= solo in scope di funzione. Fuori serve var.
Tipi base
bool // true / false int int8 int16 int32 int64 uint uint8 ... uintptr byte = uint8 // alias rune = int32 // codepoint Unicode float32 float64 string // immutabile, 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 ogni variabile dichiarata ha uno zero value: niente undefined.
Conversioni
i := 42 f := float64(i) // 42.0 s := string(rune(65)) // "A" (rune!) // stringa<->numero: usa strconv n, err := strconv.Atoi("42") s := strconv.Itoa(42)string(42) NON dà "42": dà il carattere U+002A. Usa strconv.
Controllo del flusso
if con init
if v, err := f(); err != nil { return err } else { use(v) } // niente parentesi attorno alla condizione // graffa { sulla stessa riga (gofmt obbliga)for (unico loop)
for i := 0; i < 10; i++ { ... } // classico for cond { ... } // while for { ... } // infinito for i, v := range slice { ... } for k, v := range mappa { ... } // ordine non determ. for i := range canale { ... }switch
switch x { case 1, 2: ... case 3: ... default: ... } // senza break implicito (no fallthrough di default) switch { // come if/else case a > b: ... case a < b: ... }defer
f, err := os.Open(path) if err != nil { return err } defer f.Close() // eseguito al return della funzione, // argomenti valutati subito (LIFO)
Funzioni
Definizione
func somma(a, b int) int { return a + b } // ritorni multipli + named result func dividi(a, b float64) (q float64, err error) { if b == 0 { err = errors.New("div per zero") return } q = a / b return }Variadici
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
Slice e array
Slice
s := []int{1, 2, 3} s = append(s, 4) // riassegna sempre len(s) cap(s) s2 := s[1:3] // slice condiviso, attenzione! s3 := make([]int, 0, 16) // len 0, cap 16 copy(dst, src)append può rialloccare: il valore di ritorno DEVE essere riassegnato.
Array (lunghezza fissa)
var a [3]int // [0 0 0] a := [3]int{1, 2, 3} a := [...]int{1, 2, 3} // compilatore conta // gli array sono valori: passarli per valore copia tutto
Map
Creare e usare
m := map[string]int{"a": 1, "b": 2} m["c"] = 3 v, ok := m["x"] // ok=false se mancante delete(m, "a") len(m) for k, v := range m { ... } // ordine non determ.Init obbligatorio
var m map[string]int // nil m["a"] = 1 // PANIC: write su nil map m = make(map[string]int) m["a"] = 1 // ok
Stringhe
Operazioni
s := "Ciao, mondo" len(s) // byte, non rune strings.ToUpper(s) strings.Contains(s, "mondo") strings.Split(s, ", ") strings.Join(parts, "-") strings.HasPrefix(s, "Ciao") strings.ReplaceAll(s, "o", "0")Builder per loop
var b strings.Builder for _, w := range parole { b.WriteString(w) b.WriteByte(' ') } out := b.String() // EVITA s += w in loop: O(n^2)Iterare rune (Unicode)
for i, r := range "caffè" { // i = byte offset, r = rune (int32) fmt.Printf("%d %c\n", i, r) } // len("caffè") == 6 (byte), non 5
Struct e metodi
Struct
type Punto struct { X, Y float64 } p := Punto{X: 3, Y: 4} p.X = 5 // puntatori q := &Punto{1, 2} q.X // auto-deref: niente (*q).XMetodi e receiver
func (p Punto) Distanza() float64 { return math.Hypot(p.X, p.Y) } // receiver puntatore: modifica e/o tipi grandi func (p *Punto) Trasla(dx, dy float64) { p.X += dx; p.Y += dy }Coerenza: tutti i metodi di un tipo con receiver dello stesso tipo (value o pointer).
Composizione (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() // promosso da Animale
Interfacce ed errori
Interfaccia
type Stringer interface { String() string } // soddisfatta implicitamente: nessun "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("?") }error idiomatico
if err != nil { return fmt.Errorf("apri %s: %w", path, err) } // confronto sentinelle if errors.Is(err, io.EOF) { ... } // cast tipato var pe *fs.PathError if errors.As(err, &pe) { use(pe.Path) }Sempre %w per wrappare. Mai duplicare il messaggio dell’errore originale.
Goroutine e channel
Goroutine
go funzione(arg) // fire-and-forget // sync con WaitGroup var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() fai() }() wg.Wait()Channel
ch := make(chan int) // unbuffered ch := make(chan int, 4) // buffered ch <- 42 // send v := <-ch // receive v, ok := <-ch // ok=false se chiuso close(ch) // solo dal producer for v := range ch { ... } // fino a chiusuraSolo il producer chiude. Mai chiudere un channel solo-receiver.
select
select { case v := <-ch1: use(v) case ch2 <- 42: // send se può 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, ...) // dentro work: select { case <-ctx.Done(): return ctx.Err() case res := <-out: return res }
Stdlib essenziale
fmt
fmt.Println("ciao", 42) fmt.Printf("%s = %d\n", k, v) // verbi: %v default, %+v campi, %#v Go-syntax, %T tipo, %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 magico! d := 5 * time.Second t.Add(d) time.Since(t) time.Sleep(d) // Sleep(5) sleeperebbe 5 nanosecondi 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 // non esportato: ignorato } b, err := json.Marshal(u) err = json.Unmarshal(b, &u) // PUNTATORE json.NewEncoder(w).Encode(u) json.NewDecoder(r).Decode(&u)
Testing e moduli
Test
// 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) } }) } }Benchmark
func BenchmarkX(b *testing.B) { for i := 0; i < b.N; i++ { X() } } // go test -bench=. -benchmemModuli
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: percorso completo, sempre
Generics e idiomi
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 } // constraint custom type Numero interface { ~int | ~float64 } func Sum[T Numero](nums []T) T { ... }cmp.Ordered (Go 1.21+) per <, <=, ==. comparable per ==.
Visibilita & naming
// Esportato: maiuscola iniziale type User struct{} func New() *User { ... } // Privato: minuscola type cache struct{} func calc() int { ... } // acronimi tutti maiuscoli: HTTPClient, URL, IDpanic / recover
// panic SOLO per bug irrecuperabili o init if must == nil { panic("must non nil") } // recover SOLO al bordo del sistema defer func() { if r := recover(); r != nil { log.Printf("panic: %v", r) } }()In codice normale ritorna error. Panic non sostituisce la gestione errori.