En Go, la différence entre un array et un slice est fondamentale. comprendre ce qui se passe sous le capot est essentiel pour éviter les fuites de mémoire et les effets de bord inattendus.


1. L'Array : Le bloc statique

L'array a une taille fixe définie à la compilation. En Go, un array est une valeur. Cela signifie que si vous passez un array à une fonction, Go copie l'intégralité des données.

// Un array de 4 entiers
var staticArray [4]int = [4]int{1, 2, 3, 4}

2. Le Slice : Le descripteur dynamique

Un slice est une structure légère (24 octets) qui pointe vers un array sous-jacent. Il est composé de trois éléments :

  • Pointer : L'adresse mémoire du début du slice.
  • Length (len) : Le nombre d'éléments actuels.
  • Capacity (cap) : La limite de l'array sous-jacent.
Mécanisme de réallocation : Lorsque vous utilisez append() et que la capacité est dépassée, Go alloue un nouvel array (souvent de taille double), y copie les données et redirige le pointeur.

3. Pointeur d'Array vs Slice : Le débat

Vous pourriez vous demander : "Si je passe un pointeur d'array (*[N]int), c'est aussi performant qu'un slice ?"

La réponse est oui, mais c'est rigide.

Caractéristique Pointeur d'Array (*[3]int) Slice ([]int)
Poids (paramètre) 8 octets 24 octets
Flexibilité Nul (taille fixe) Totale (taille variable)
Usage Cryptographie, calcul bas-niveau Standard Go

Le slice est souvent préféré car il offre la puissance du pointeur (pas de copie des données lourdes) tout en permettant d'utiliser des fonctions génériques quelle que soit la taille de la structure.


4. Erreurs classiques et Gestion mémoire

A. La fuite de mémoire (Memory Leak)

Si vous créez un petit slice à partir d'un array gigantesque, l'array géant reste en mémoire tant que le slice existe.

// MAUVAIS : L'array de 1Go survit en mémoire
func getSmall() []byte {
    bigData := make([]byte, 1<<30) 
    return bigData[:10] 
}

// BIEN : On copie pour libérer le gros bloc
func getSmallFixed() []byte {
    bigData := make([]byte, 1<<30)
    res := make([]byte, 10)
    copy(res, bigData[:10])
    return res
}

B. L'effet de bord inattendu

Puisque le slice pointe vers un array, modifier un slice peut en modifier un autre s'ils partagent le même array sous-jacent.

a := []int{1, 2, 3}
b := a[:2]
b[0] = 999 
// a[0] vaut maintenant 999 !

Conclusion

Privilégiez les slices pour leur flexibilité. Si vous connaissez la taille finale, utilisez make([]type, len, cap) pour éviter les réallocations inutiles. Gardez les pointeurs d'arrays pour les cas où la taille est une contrainte métier stricte et immuable.