Un atelier Rust · Édition d'ApprentissageA Rust Workshop · Learning Edition

clone, Cow & l'allocation cachée clone, Cow & the hidden allocation

Le .clone() pour faire taire le compilateur cache une allocation — souvent un emprunt qu'on n'a pas su écrire. Cow n'alloue que si l'on mute vraiment. The .clone() to silence the compiler hides an allocation — often a borrow you couldn't express. Cow allocates only if you actually mutate.

AudienceAudience
Dev maîtrisant possession et emprunts (Vol 1), à l'aise avec String/&str Dev fluent in ownership and borrows (Vol 1), comfortable with String/&str
Format
Self-paced
ChapitresChapters
5
Date
Juin 2026 Jun 2026
≈ 18 min ●●●○ CoûtCowclone

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

L'opposé de l'abstraction effacée : .clone() fait un vrai travail.The opposite of the erased abstraction: .clone() does real work.

Le numéro précédent montrait une abstraction que le compilateur efface : la chaîne d'itérateurs ne coûte rien à l'exécution. .clone() est son exact contraire — une opération qui fait un travail bien réel, et qui ne s'évapore pas. Pour un type qui possède de la mémoire sur le tas (String, Vec, HashMap), cloner veut dire allouer un nouveau tampon et y recopier tous les octets. Venant d'un langage à GC, on n'y prête aucune attention : là-bas, « passer » un objet ne le copie pas, on partage une référence et le ramasse-miettes gère le reste. En Rust, .clone() est explicite précisément parce qu'il a un coût qu'on doit voir. Le danger est de le dégainer par réflexe — chaque fois qu'un emprunt gêne, qu'un move dérange : un petit .clone() et ça compile. Chacun est une allocation silencieuse, et le plus souvent le signe d'un emprunt qu'on n'a pas su écrire.The previous issue showed an abstraction the compiler erases: the iterator chain costs nothing at runtime. .clone() is its exact opposite — an operation that does very real work, and doesn't evaporate. For a type owning heap memory (String, Vec, HashMap), cloning means allocating a new buffer and copying all the bytes into it. Coming from a GC language, you pay it no attention: there, 'passing' an object doesn't copy it, you share a reference and the garbage collector handles the rest. In Rust, .clone() is explicit precisely because it has a cost you must see. The danger is drawing it by reflex — every time a borrow is awkward, a move is bothersome: a little .clone() and it compiles. Each one is a silent allocation, and most often the sign of a borrow you couldn't express.

Cloner duplique le tampon ; emprunter ne fait que pointerCloning duplicates the buffer; borrowing only points
original.clone()original.clone()
original (pile)
tas: « un long texte… »
copie (pile)
tas: « un long texte… »

2 tampons sur le tas — octets dupliqués2 heap buffers — bytes duplicated

&original&original
original (pile)
tas: « un long texte… »
vue: &str (pile)
──▸pointe le même tamponpoints at the same buffer

1 seul tampon — une référence qui pointe1 buffer only — a reference that points

Ce que coûte un clone, selon le typeWhat a clone costs, by type
let original = String::from("un assez long texte…");

let copie = original.clone();   // ALLOUE un nouveau tampon sur le tas + recopie tous les octets
// `original` et `copie` sont deux String distinctes, chacune possède SON tampon.

let n: i32 = 42;
let m = n;                      // pour un type Copy, « copier » est une copie de bits — zéro tas.
// le coût de clone() dépend du type : trivial pour Copy (i32, bool), une ALLOCATION
// pour String, Vec, HashMap… — tout ce qui possède de la mémoire sur le tas.

Le mot .clone() recouvre deux réalités très différentes. Pour un type Copy (un i32, un bool, un char), il n'y a pas de tas : la valeur tient sur la pile, la « copier » est un transfert de quelques octets, gratuit — d'ailleurs on écrit juste let m = n;. Pour un type qui possède du tas, c'est une allocation plus une recopie, proportionnelle à la taille des données. La distinction Copy / Clone n'est pas cosmétique : Copy est implicite et toujours trivial ; Clone est explicite parce qu'il peut être cher. Voir le .clone(), c'est voir le coût.The word .clone() covers two very different realities. For a Copy type (an i32, a bool, a char), there's no heap: the value sits on the stack, 'copying' it is a few-byte transfer, free — indeed you just write let m = n;. For a type that owns heap, it's an allocation plus a copy, proportional to the data's size. The Copy / Clone distinction isn't cosmetic: Copy is implicit and always trivial; Clone is explicit because it can be costly. Seeing the .clone() is seeing the cost.

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

« Ce .clone() est-il un vrai besoin de posséder une seconde copie — ou un contournement du borrow checker ? » La plupart des clones qu'on ajoute pour « faire passer » le compilateur trahissent un emprunt qu'on aurait pu écrire. Le bon réflexe : devant un .clone() réflexe, se demander d'abord si un & ne suffirait pas. Cloner reste parfois la bonne réponse — mais ce doit être un choix, pas un automatisme pour calmer une erreur.'Is this .clone() a genuine need to own a second copy — or a borrow-checker workaround?' Most clones added to 'make the compiler pass' betray a borrow you could have written. The right reflex: faced with a reflexive .clone(), first ask whether a & wouldn't do. Cloning is sometimes the right answer — but it must be a choice, not an automatism to quiet an error.

Copy vs Clone, la frontièreCopy vs Clone, the boundary

Un type est Copy si le dupliquer revient à copier ses bits, sans rien à allouer ni libérer : les entiers, les flottants, les booléens, les tuples de Copy. La copie est alors implicite (let m = n laisse n utilisable) et toujours gratuite. Tout ce qui possède une ressource — un tampon sur le tas (String, Vec), un handle de fichier — n'est PAS Copy : le dupliquer demande un travail, donc un appel explicite à .clone(). C'est pourquoi Copy s'obtient par dérivation seulement quand c'est trivial, et que Clone, lui, est toujours visible dans le code.A type is Copy if duplicating it amounts to copying its bits, with nothing to allocate or free: integers, floats, booleans, tuples of Copy. The copy is then implicit (let m = n leaves n usable) and always free. Anything owning a resource — a heap buffer (String, Vec), a file handle — is NOT Copy: duplicating it requires work, hence an explicit .clone() call. That's why Copy is derivable only when trivial, and Clone is always visible in the code.

🔒

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

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

Retour au kiosqueBack to newsstand