append n'ajoute pas à ta slice. Il en retourne une nouvelle — que tu dois récupérer.append doesn't add to your slice. It returns a new one — which you must keep.
Le numéro précédent a posé la slice comme une vue de trois mots sur un tableau, et l'a laissée immobile. append est l'opération qui la fait grandir — et c'est là que tout se complique. Le piège commence par sa signature : append ne modifie pas la slice en place, il en renvoie une, qu'il faut réassigner. La forme s = append(s, x) n'est pas un tic de style ; elle est obligatoire, parce que append peut être amené à changer les trois mots de l'en-tête. Pourquoi ? Parce qu'un tableau a une taille fixe. Tant qu'il reste de la place — de la capacité — append écrit dans le tableau existant et se contente d'augmenter la longueur. Mais dès que le tableau est plein, il n'y a pas de « l'agrandir sur place » : append doit allouer un tableau neuf, plus grand, y recopier tout, et te rendre un en-tête qui pointe désormais ailleurs. Deux comportements radicalement différents, derrière un seul appel — et c'est à toi de toujours récupérer le résultat, parce que tu ne sais pas lequel des deux vient d'avoir lieu.The previous issue laid down the slice as a three-word view over an array, and left it still. append is the operation that grows it — and that's where everything gets complicated. The trap starts with its signature: append doesn't modify the slice in place, it returns one, which you must reassign. The form s = append(s, x) isn't a style tic; it's mandatory, because append may have to change all three words of the header. Why? Because an array has a fixed size. While there's room — capacity — append writes into the existing array and merely bumps the length. But the moment the array is full, there's no 'grow it in place': append must allocate a new, larger array, copy everything into it, and hand you a header now pointing elsewhere. Two radically different behaviors behind a single call — and it's on you to always keep the result, because you don't know which of the two just happened.
func push(s []int, x int) { s = append(s, x) // met à jour la COPIE LOCALE de l'en-tête } // ce nouvel en-tête meurt à la fin de push s := make([]int, 0, 4) push(s, 1) push(s, 2) fmt.Println(s, len(s)) // [] 0 — l'appelant n'a rien vu.
func push(s []int, x int) []int { return append(s, x) // on RENVOIE le nouvel en-tête } s := make([]int, 0, 4) s = push(s, 1) // … et l'appelant le RÉASSIGNE s = push(s, 2) fmt.Println(s, len(s)) // [1 2] 2 — la règle : le résultat remonte jusqu'à toi.
Le même append(s, x) emprunte l'un ou l'autre chemin selon une seule donnée : reste-t-il de la capacité ? Tu ne le vois pas dans le code — cap n'apparaît nulle part — et pourtant c'est lui qui décide si ton tableau est partagé ou détaché. D'où l'unique règle de survie : s = append(s, x), toujours.The same append(s, x) takes one path or the other by a single datum: is there capacity left? You don't see it in the code — cap appears nowhere — yet it decides whether your array is shared or detached. Hence the one survival rule: s = append(s, x), always.
« append rend un en-tête ; je le réassigne, et je me méfie du tableau d'origine. » Le numéro 1 a montré le partage immobile ; celui-ci montre l'opération qui peut le rompre — ou pire, l'exploiter pour corrompre. Le bug le plus subtil de Go vit ici, sur la frontière entre « écrit en place » et « réalloue », une frontière qu'aucune ligne ne rend visible. Tout le numéro consiste à la rendre lisible."append returns a header; I reassign it, and I distrust the original array." Issue 1 showed the still sharing; this one shows the operation that can break it — or worse, exploit it to corrupt. Go's subtlest bug lives here, on the boundary between "writes in place" and "reallocates," a boundary no line makes visible. The whole issue is about making it readable.
Parce qu'un tableau, en mémoire, est un bloc contigu de taille fixe : ses éléments se suivent sans trou. « L'agrandir » voudrait dire que les octets juste après lui sont libres — ce que rien ne garantit ; ils appartiennent peut-être déjà à autre chose. La seule opération sûre est donc d'allouer un bloc neuf, assez grand, et d'y recopier l'existant. C'est pour ça qu'append ne peut pas promettre d'écrire en place : il le fait quand la capacité pré-réservée le permet, et bascule sur une réallocation sinon. Toute la mécanique du numéro découle de cette contrainte physique — la mémoire contiguë ne s'étire pas, elle se recopie ailleurs.Because an array, in memory, is a fixed-size contiguous block: its elements follow with no gap. 'Growing it' would mean the bytes right after it are free — which nothing guarantees; they may already belong to something else. So the only safe operation is to allocate a fresh block, big enough, and copy the existing into it. That's why append can't promise to write in place: it does when pre-reserved capacity allows, and falls back to a reallocation otherwise. The whole issue's machinery follows from that physical constraint — contiguous memory doesn't stretch, it's recopied elsewhere.