ProgrammazioneSviluppatore Backend

Descrivi le caratteristiche del lavoro con gli slice in Go: che cos'è un nil slice, qual è la differenza tra length e capacity e come aumentare correttamente uno slice senza perdite di dati o panico?

Supera i colloqui con l'assistente IA Hintsage

Risposta.

Lo slice è un array dinamico, l'unità strutturale principale quando si lavora con gli array in Go. Storicamente, i linguaggi di programmazione offrivano array di dimensioni fisse o strutture più pesanti. Go ha implementato gli slice come uno strumento comodo per gestire collezioni di memoria con gestione automatica delle dimensioni e possibilità di modificare la lunghezza.

Il problema consiste nella corretta gestione della memoria, nella gestione dei casi limite (slice vuoti, nil, pieni) e nella comprensione della differenza tra lunghezza e capacità dello slice.

La soluzione è usare correttamente le funzioni incorporate len(), cap(), e operare sugli slice secondo le convenzioni di Go.

Esempio di codice:

var a []int // nil slice, len=0, cap=0 b := make([]int, 0) // slice vuoto, len=0, cap=0 c := make([]int, 3, 5) // len=3, cap=5 c = append(c, 4, 5, 6) // cap verrà aumentata automaticamente

Caratteristiche chiave:

  • Nil slice (var s []int) non alloca affatto memoria, si differenzia dallo slice vuoto (make([]int, 0)) solo internamente.
  • Length mostra il numero attuale di elementi, capacity — il massimo senza ulteriore allocazione.
  • Durante append, se la capacity non è sufficiente, ne viene creato uno nuovo dietro le quinte, mentre i dati precedenti rimangono invariati.

Domande trabocchetto.

Qual è la lunghezza e la capacità dello slice dopo append a un nil slice?

Nil slice (var s []int) dopo append(s, 1) diventa uno slice di lunghezza 1, capacità 1 — Go gestisce automaticamente l'allocazione.

var s []int s = append(s, 42) // s ora [42], len=1, cap=1

È possibile accedere alla capacity di uno slice pari a nil?

Sì, per il nil slice entrambe le funzioni — len e cap — restituiscono 0. Non ci sarà panico.

var s []int fmt.Println(len(s), cap(s)) // 0 0

Cosa succede se si prova a assegnare a un elemento per indice senza un’allocazione preventiva di memoria a uno slice pari a nil?

Panico index out of range, perché lo slice non ha elementi.

var s []int s[0] = 1 // panic: runtime error: index out of range

Errori tipici e anti-pattern

  • Errori nel passare nil slice o slice vuoti a una funzione che richiede una lunghezza specifica.
  • Riempimento eccessivo dello slice senza considerare la crescita della capacità e cicli scritti in modo illogico con append.
  • Tentativi di assegnazione per indice invece di append per aggiungere elementi.

Esempio dalla vita reale

Caso negativo

Il team ha deciso di utilizzare solo var s []T nel server Go, aspettandosi che fosse sempre equivalente a make([]T, 0). Di conseguenza, alcune serializzazioni JSON restituivano null anziché [].

Vantaggi:

  • Meno allocazioni di memoria all'inizio.

Svantaggi:

  • Malfunzionamento della JSON (conseguentemente arrivava null invece di un array).
  • Errori di esecuzione durante l'accesso per indice.

Caso positivo

Utilizzo di make([]T, 0, 100) per preallocazione, e successivamente solo append in un ciclo. In questo modo si minimizzano le allocazioni di memoria e si guadagna spesso in prestazioni.

Vantaggi:

  • Efficiente gestione della memoria.
  • Nessun panico e valori inaspettati durante la serializzazione.

Svantaggi:

  • Possibile spreco di memoria se il numero atteso di elementi non coincide con la capacità specificata.