Go — Cheat Sheet

Tag: go, golang, backend, concurrency, goroutines, channels

Livello: intermedio

TL;DR: Prontuario sintetico su Go per colloqui tecnici: goroutine, channel, gestione errori esplicita, struct e interfacce implicite.

Concetti chiave

Compilato, tipizzato staticamente, con garbage collection e sintassi essenziale.

Concorrenza nativa basata su CSP tramite goroutine e channel.

Le goroutine hanno stack iniziali leggeri (pochi KB) e crescono dinamicamente.

Channel non bufferizzati sincronizzano lettura e scrittura in modo bloccante.

Channel bufferizzati consentono invii non bloccanti fino a capienza massima.

Assenza di classi ed ereditarieta: polimorfismo gestito tramite composition e interface.

Implementazione delle interfacce strutturale e implicita: zero keyword implements.

Gestione errori esplicita: le funzioni restituiscono valori di tipo error.

Zero value assegnato automaticamente a ogni variabile non inizializzata.

Istruzione defer esegue una funzione all'uscita dal blocco circostante.

Domande tipiche e risposte pronte

Q: Qual e la differenza tra goroutine e thread di sistema?R: La goroutine e gestita dal runtime di Go con scheduling M:N su thread OS. Richiede circa 2 KB di memoria iniziale contro i megabyte del thread di sistema.

Q: Come gestisci la sincronizzazione tra piu goroutine?R: Uso sync.WaitGroup per attendere completamenti multipli, oppure channel per lo scambio di messaggi e context.Context per propagare annullamenti e timeout.

Q: Come funziona il polimorfismo in Go senza ereditarieta?R: Definisco un'interfaccia con i metodi necessari. Qualsiasi struct che implementa tali firme soddisfa automaticamente l'interfaccia senza dichiarazioni esplicite.

Q: Quando conviene usare un puntatore receiver rispetto a un value receiver?R: Uso un puntatore receiver se il metodo deve mutare lo stato della struct, oppure per evitare copie costose di struct pesanti in memoria.

Q: Come si gestiscono i panic in produzione?R: Riservo panic per errori irrecuperabili all'avvio. Per isolare blocchi critici uso recover() all'interno di una funzione differita tramite defer.

Insidie e errori comuni

Goroutine leak causato da channel mai letti o mai chiusi.

Data race su variabili condivise senza sincronizzazione con mutex o channel.

Invio su channel chiuso genera un panic immediato a runtime.

Chiusura di un channel gia chiuso scatena un panic.

Scrittura o lettura su un channel nil blocca la goroutine indefinitamente.

Modifica della variabile di iterazione in closure concorrenti senza ridefinizione locale.

Uso improprio di defer all'interno di loop lunghi: posticipa il rilascio risorse.

Confusione tra slice e array: lo slice e una vista su array sottostante.

Snippet e comandi

// Esempio worker con sync.WaitGroup e channel
package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

func worker(ctx context.Context, id int, jobs <-chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			return
		case job, ok := <-jobs:
			if !ok {
				return
			}
			fmt.Printf("Worker %d: job %d\n", id, job)
		}
	}
}

Bash

# Esecuzione test con race detector
go test -race ./...

# Verifica e pulizia delle dipendenze del modulo
go mod tidy

# Compilazione binario statico
CGO_ENABLED=0 GOOS=linux go build -o app ./main.go

Da ripassare

[ ] Meccaniche del memory model e race detector di Go

[ ] Differenze tra sync.Mutex e sync.RWMutex

[ ] Pattern di cancellazione con context.WithTimeout

[ ] Anatomia interna di slice, map e channel

[x] Sintassi e utilizzo dell'istruzione defer

[x] Dichiarazione di struct e metodi con receiver

Jarvis Interviews
Jarvis