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

La copie qui casse The copy that breaks

Copier une struct qui contient un sync.Mutex, c'est copier un verrou à moitié pris — et go vet te l'interdit pour de bonnes raisons. WaitGroup, slices partagées : certains types ne survivent pas à une copie. La règle copylocks, et pourquoi. Copying a struct that holds a sync.Mutex copies a half-held lock — and go vet forbids it for good reason. WaitGroup, shared slices: some types don't survive a copy. The copylocks rule, and why.

AudienceAudience
Dev dont le mutex ne protège rien ou dont la WaitGroup bloque, sans comprendre que c'est une copie Dev whose mutex protects nothing or whose WaitGroup blocks, not realizing it's a copy
Format
Self-paced
ChapitresChapters
5
Date
Nov 2027 Nov 2027
≈ 15 min ●●●○ CopieMutexgo vet

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

Tout est copié — sauf ce qui ne survit pas à la copie. Un verrou dupliqué ne protège plus rien.Everything is copied — except what doesn't survive the copy. A duplicated lock protects nothing anymore.

Tout ce volume a célébré la copie : elle isole, elle protège, elle rend une fonction incapable de muter ton original sans le demander. Mais il existe une famille de types pour lesquels copier n'est pas un service neutre — c'est une faute. Ce sont les types qui portent un état d'identité, un état dont l'intérêt même est qu'il n'existe qu'en un seul exemplaire. Le cas archétypal est le verrou. Un sync.Mutex n'est pas une donnée inerte qu'on peut dupliquer : c'est un petit automate qui mémorise s'il est pris ou libre, et qui tient la file des goroutines en attente. Sa raison d'être est de coordonner plusieurs accès à travers un point unique. Le copier, c'est créer un second verrou, indépendant du premier, qui ignore tout de ce que l'original protège — et si on le copie pendant qu'il est pris, on fabrique un verrou figé dans un état incohérent. Deux verrous là où il en fallait un, c'est zéro exclusion mutuelle. Le langage connaît ce danger et l'a outillé : go vet embarque une analyse, copylocks, dont l'unique métier est de détecter qu'un type contenant un verrou est passé ou copié par valeur, et de te prévenir. C'est la contre-indication formelle de la sémantique de valeur — la seule zone où « tout est copié » devient « ceci, surtout pas ».This whole volume celebrated the copy: it isolates, it protects, it makes a function unable to mutate your original without asking. But there's a family of types for which copying isn't a neutral service — it's a fault. These are the types carrying an identity state, a state whose whole point is that it exists in a single instance. The archetypal case is the lock. A sync.Mutex isn't inert data you can duplicate: it's a small automaton remembering whether it's held or free, and keeping the queue of waiting goroutines. Its reason for being is to coordinate several accesses through a single point. Copying it is creating a second lock, independent of the first, ignorant of everything the original protects — and if you copy it while it's held, you fabricate a lock frozen in an inconsistent state. Two locks where one was needed is zero mutual exclusion. The language knows this danger and has tooled it: go vet ships an analysis, copylocks, whose sole job is to detect that a type containing a lock is passed or copied by value, and to warn you. It's the formal contraindication of value semantics — the one zone where 'everything is copied' becomes 'this, above all, not.'

Un verrou copié par mégarde, et go vet qui le voitA lock accidentally copied, and go vet that sees it
type Safe struct {
    mu    sync.Mutex     // un verrou : tient un état (pris / libre + file)
    count int
}

func (s Safe) Inc() {    // receveur VALEUR → s est une COPIE de la struct…
    s.mu.Lock()          // … donc on verrouille le mutex de la copie, pas l'original
    s.count++
    s.mu.Unlock()
}
// $ go vet
// Inc passes lock by value: Safe contains sync.Mutex
Deux familles de typesTwo families of types
se copie librement
int, string, struct de données, time.Time — une copie vaut l'original
|
ne se copie pas
sync.Mutex, RWMutex, WaitGroup, Once, atomic.* — et tout ce qui les contient

La frontière est claire : un type est non-copiable dès qu'il tient un état que la copie dédoublerait — un verrou, un compteur de synchronisation, une garantie d'unicité. Dès qu'une struct embarque l'un d'eux, elle hérite de l'interdiction. Et go vet la fait respecter.The boundary is clear: a type is non-copyable as soon as it holds a state the copy would split — a lock, a synchronization counter, a uniqueness guarantee. The moment a struct embeds one of them, it inherits the ban. And go vet enforces it.

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

« Ce type contient-il un verrou ? alors il ne circule que par pointeur, jamais par valeur. » C'est le corollaire dur de tout le volume. Le numéro 2 a montré qu'on choisit souvent le receveur pointeur ; ici, on n'a plus le choix. Un type à verrou impose des receveurs pointeur, un passage par *T, et une vigilance sur chaque endroit — affectation, range, retour de fonction — où une copie pourrait se glisser. go vet est l'ami qui les repère pour toi."Does this type contain a lock? then it travels only by pointer, never by value." It's the hard corollary of the whole volume. Issue 2 showed we often choose the pointer receiver; here, we no longer have the choice. A lock type imposes pointer receivers, a pass by *T, and vigilance at every spot — assignment, range, function return — where a copy could slip in. go vet is the friend that spots them for you.

Pourquoi un verrou est un état, pas une donnéeWhy a lock is a state, not data

Une donnée décrit quelque chose : un nombre, un nom, une date. La copier produit une seconde description, tout aussi valable. Un verrou ne décrit rien — il coordonne. Son contenu, c'est la trace vivante d'un accord entre goroutines : qui le détient à cet instant, qui patiente. Cette trace n'a de sens qu'au singulier, en un point partagé que tout le monde regarde. La dupliquer, c'est comme photocopier le bâton de parole d'une assemblée : on se retrouve avec deux bâtons, et plus personne ne sait qui a la parole. C'est pourquoi ces types embarquent souvent un marqueur interne « ne pas copier » que go vet sait lire : le langage encode, dans le type lui-même, qu'il appartient à la famille des identités, pas à celle des valeurs.Data describes something: a number, a name, a date. Copying it produces a second, equally valid description. A lock describes nothing — it coordinates. Its content is the living trace of an agreement between goroutines: who holds it at this instant, who waits. That trace only makes sense in the singular, at a shared point everyone watches. Duplicating it is like photocopying an assembly's talking stick: you end up with two sticks, and no one knows who has the floor. That's why these types often embed an internal 'do not copy' marker that go vet can read: the language encodes, in the type itself, that it belongs to the family of identities, not that of values.

🔒

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