Vol. 4 — № 02
The Go Loop · KiosqueNewsstand
Blog  
Un atelier Go · Édition d'ApprentissageA Go Workshop · Learning Edition

Envelopper, dérouler Wrap, unwrap

fmt.Errorf("%w", err) emballe une erreur dans un contexte sans la perdre ; errors.Is et errors.As la retrouvent au fond de la pile. Sentinelle ou typée, l'essentiel est de ne jamais casser la chaîne. fmt.Errorf("%w", err) wraps an error in context without losing it; errors.Is and errors.As find it back at the bottom of the stack. Sentinel or typed, the point is never to break the chain.

AudienceAudience
Dev qui sait rendre une erreur, mais bute sur errors.Is / errors.As et le couple %w / %v Dev who can return an error, but trips on errors.Is / errors.As and the %w / %v pair
Format
Self-paced
ChapitresChapters
5
Date
Avr 2027 Apr 2027
≈ 16 min ●●○○ ErreursWrappingerrors.Is

Chapitre 1 en accès libre — la suite (ch. 2 à 5) est réservée. Chapter 1 free to read — the rest (ch. 2–5) is members-only.

01CadrageFraming3 min

Une erreur est une valeur. Et comme toute valeur, on peut l'emboîter dans une autre.An error is a value. And like any value, you can nest it inside another.

Le numéro précédent a posé la règle : en chemin, on n'ajoute que du contexte, on ne traite qu'au sommet. Reste à savoir comment ajouter ce contexte sans rien perdre. Deux verbes s'opposent. Aplatir : fmt.Errorf avec %v transforme l'erreur en texte et la fond dans un message — lisible, mais définitif, l'original n'existe plus. Envelopper : fmt.Errorf avec %w garde l'erreur d'origine vivante, attachée sous la nouvelle, formant une chaîne. Le message affiché est le même ; la différence est invisible à l'écran et décisive dans le code. Une erreur aplatie est une impasse. Une erreur enveloppée est une pile qu'on pourra dérouler, couche après couche, jusqu'à la cause première.The previous issue set the rule: along the way you only add context, you handle only at the top. What remains is how to add that context without losing anything. Two verbs oppose each other. Flatten: fmt.Errorf with %v turns the error into text and melts it into a message — readable, but final, the original no longer exists. Wrap: fmt.Errorf with %w keeps the original error alive, attached beneath the new one, forming a chain. The displayed message is identical; the difference is invisible on screen and decisive in code. A flattened error is a dead end. A wrapped error is a stack you can unroll, layer after layer, down to the first cause.

Aplatir avec %v : le message survit, l'erreur meurtFlatten with %v: the message survives, the error dies
// %v : on aplatit l'erreur en texte. L'original disparaît dans la chaîne.
if err != nil {
    return fmt.Errorf("load config: %v", err)   // %v = juste du texte
}
// l'appelant lit un beau message… mais ne peut plus retrouver
// l'erreur d'origine : os.ErrNotExist s'est dissous dans une string.
Envelopper avec %w : le message ET l'erreur surviventWrap with %w: the message AND the error survive
// %w : on EMBALLE l'erreur. L'original reste accessible, une couche dessous.
if err != nil {
    return fmt.Errorf("load config: %w", err)   // %w = on chaîne
}
// même message à l'écran, mais l'erreur d'origine est conservée :
// errors.Is(err, os.ErrNotExist) la retrouvera au fond de la pile.
Envelopper, c'est chaînerWrapping is chaining
load config
couche 3 · ce que tu lis
open /etc/app.conf
couche 2 · contexte
os.ErrNotExist
couche 1 · la cause

Chaque %w ajoute une couche par-dessus la précédente, sans écraser ce qu'il y a dessous. Le résultat est une liste chaînée d'erreurs : tu lis la couche du dessus, mais tu peux descendre jusqu'à la cause. Unwrap() retire une couche ; errors.Is et errors.As descendent jusqu'au bout. La chaîne est une structure de données — et on la parcourt.Each %w adds a layer over the previous one, without crushing what lies beneath. The result is a linked list of errors: you read the top layer, but you can descend to the cause. Unwrap() peels one layer; errors.Is and errors.As go all the way down. The chain is a data structure — and you walk it.

Le réflexe du numéroThe issue's reflex

« En montant, j'enveloppe avec %w ; en descendant, je déroule avec Is et As. » Le numéro défend une idée simple : tant que la chaîne n'est pas cassée, n'importe quelle couche peut interroger n'importe quelle couche du dessous. Casser la chaîne — un %v de trop — c'est jeter cette possibilité, exactement comme un _ = jetait l'erreur entière au numéro précédent."On the way up, I wrap with %w; on the way down, I unroll with Is and As." The issue argues one simple thing: as long as the chain isn't broken, any layer can interrogate any layer beneath it. Breaking the chain — one %v too many — throws that possibility away, exactly as a _ = threw the whole error away last issue.

Un %w par appel, pas plus.One %w per call, no more.

Dans l'usage courant, fmt.Errorf n'a qu'un verbe %w par message : une erreur enveloppe une autre erreur. C'est cohérent avec l'idée de chaîne — une liste a un seul suivant. Depuis Go 1.20, Go sait aussi faire des arbres : fmt.Errorf accepte plusieurs %w, et errors.Join rassemble plusieurs erreurs (toutes les lignes invalides d'un fichier, par exemple) en une seule — errors.Is et errors.As savent les traverser. Mais le cas courant, de loin, reste la chaîne simple : une cause, du contexte ajouté à chaque étage, une couche par return.In everyday use, fmt.Errorf has a single %w verb per message: one error wraps one other error. That fits the chain idea — a list has a single next. Since Go 1.20, Go can build trees too: fmt.Errorf accepts several %w, and errors.Join gathers several errors (every invalid line of a file, say) into one — errors.Is and errors.As can traverse them. But the common case, by far, stays the simple chain: one cause, context added at each floor, one layer per return.

🔒

La suite est réservée The rest is members-only

Le premier numéro est libre. Débloque tout The Go Loop — tous les volumes, à vie — pour 5 €, paiement unique. The first issue is free. Unlock all of The Go Loop — every volume, forever — for €5, one-time.

Retour au kiosqueBack to newsstand