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

L'interface nil qui n'est pas nil The nil interface that isn't nil

Un *T nil rangé dans une interface n'est pas == nil : l'interface porte un type, donc elle est non vide. Le piège trahit le réflexe le plus sacré de Go — if err != nil — et fait passer une erreur absente pour présente. On le voit, on l'explique, on l'évite. A nil *T stored in an interface is not == nil: the interface carries a type, so it's non-empty. The trap betrays Go's most sacred reflex — if err != nil — making an absent error look present. We make it visible, explain it, avoid it.

AudienceAudience
Dev qui écrit if err != nil tous les jours et veut comprendre pourquoi il peut mentir Dev who writes if err != nil daily and wants to know why it can lie
Format
Self-paced
ChapitresChapters
5
Date
Jan 2027 Jan 2027
≈ 16 min ●●●○ nilInterfacesPiège

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

Un pointeur nil rangé dans une interface donne une interface qui n'est pas nil.A nil pointer stored in an interface yields an interface that is not nil.

Au numéro précédent, on a vu qu'une valeur d'interface est une paire (type, valeur). C'était une jolie image ; c'est aussi un piège. Car cette paire change le sens de nil. Un pointeur peut être nil — c'est une valeur parfaitement ordinaire. Mais dès qu'on range ce pointeur nil dans une interface, la case « type » se remplit avec son type concret, et l'interface, elle, cesse d'être vide. La comparaison i == nil devient fausse alors même que ce que l'interface contient est nil. C'est le bug le plus déroutant de Go, parce qu'il fait mentir nil — et nil, on lui fait confiance partout.Last issue we saw that an interface value is a (type, value) pair. It was a tidy image; it's also a trap. Because that pair changes the meaning of nil. A pointer can be nil — a perfectly ordinary value. But the moment you store that nil pointer in an interface, the 'type' slot fills with its concrete type, and the interface stops being empty. The comparison i == nil becomes false even though what the interface holds is nil. It's Go's most baffling bug, because it makes nil lie — and nil is something we trust everywhere.

Le mensonge : un nil qui compare fauxThe lie: a nil that compares false
func find() *Node { return nil }     // renvoie un *Node bel et bien nil

var i any = find()                    // on le range dans une interface
fmt.Println(i == nil)                 // false  ← surprise

// le *Node EST nil. L'interface qui le transporte, elle, ne l'est pas.
Le modèle : nil ⇔ les deux cases videsThe model: nil ⇔ both slots empty
// une valeur d'interface = DEUX cases : (type, valeur)
//   var i any            →  (nil,   nil)   →  i == nil   ✓ vrai
//   var p *Node = nil
//   i = p               →  (*Node, nil)   →  i == nil   ✗ faux
// nil ne compare vrai que si les DEUX cases sont vides à la fois.
Les deux cases d'une interfaceAn interface's two slots
type
nil
value
nil
== nil → true
type
*Node
value
nil
== nil → false

À gauche, l'interface vraiment vide : les deux cases à nil, donc == nil vrai. À droite, la même valeur nulle, mais la case « type » porte *Node — l'interface n'est plus vide. Une interface est nil quand son type l'est, pas quand sa valeur l'est.On the left, the truly empty interface: both slots nil, so == nil is true. On the right, the same null value, but the 'type' slot carries *Node — the interface is no longer empty. An interface is nil when its type is, not when its value is.

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

« nil dans une interface ne teste pas la valeur, il teste le type. » Tout le numéro découle de là : pourquoi if err != nil peut trahir, comment l'éviter à la source, et pourquoi le problème dépasse de loin les erreurs. La règle pratique tient en une ligne — ne range jamais un pointeur typé nil dans une interface en espérant qu'elle reste nil."nil in an interface doesn't test the value, it tests the type." The whole issue follows: why if err != nil can betray you, how to avoid it at the source, and why the problem reaches far beyond errors. The practical rule fits on one line — never store a typed nil pointer in an interface and expect it to stay nil.

Pourquoi deux cases, et pas une ?Why two slots, not one?

Parce que l'interface doit savoir QUEL type elle transporte pour appeler la bonne méthode — c'est la satisfaction implicite du numéro précédent qui l'exige. La case « type » n'est pas un caprice : c'est elle qui permet à un même io.Writer d'aiguiller vers Write d'un fichier ou d'un buffer. Le prix de cette souplesse, c'est qu'un type non vide suffit à rendre l'interface non vide — même si la valeur, derrière, est absente. Le typed nil n'est pas un bug du langage : c'est la rançon d'une représentation qui doit retenir le type.Because the interface must know WHICH type it carries to call the right method — the implicit satisfaction of last issue demands it. The 'type' slot isn't a whim: it's what lets one io.Writer dispatch to a file's Write or a buffer's. The price of that flexibility is that a non-empty type alone makes the interface non-empty — even if the value behind it is absent. The typed nil isn't a language bug: it's the ransom of a representation that must remember the type.

🔒

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