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.
2 tampons sur le tas — octets dupliqués2 heap buffers — bytes duplicated
1 seul tampon — une référence qui pointe1 buffer only — a reference that points
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.
« 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.
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.