Ask
29

I add .clone() until it compiles — when is that fine and when is it hiding a design mistake?

Six months into Rust after years of garbage-collected languages. My programs work and my strategy for the borrow checker is, honestly, to clone things until the errors stop.

I know this is the thing everyone says not to do. What I cannot tell is whether it is actually harmful in my case or whether it is a stylistic complaint. The programs are not slow. The clones are mostly small strings and small structs.

But I also have a feeling I am not learning the thing I am supposed to be learning, and that some of these clones are papering over a structure that is wrong rather than a lifetime that is awkward.

How do experienced people tell the difference between a clone that is fine and a clone that is a symptom?

2 answers Share
Report

Answering anonymously — a moderator will review it first.

  • @clone_is_fine_cal · 3h ago

    Most of your clones are fine, and the advice you have absorbed is aimed at a different problem than the one you have.

    Cloning a small string or a small struct is cheap. An allocation and a copy of a few dozen bytes. In a program that is doing anything at all — reading a file, making a request, touching a database — this is invisible. People who tell you never to clone are usually thinking about tight loops and large data, and the advice gets repeated without the context.

    So the first question is not stylistic, it is where is it. A clone in a startup path, a config load, or a request handler that runs a thousand times a second on a small string: fine. A clone of a large collection inside a loop that runs a million times: not fine, and you would see it.

    Where clone genuinely signals a design problem:

    • You are cloning to get around ownership you have not decided on. Two parts of the program both think they own the same thing, and clone lets both pretend. This is the real one.
    • You clone a large structure to read one field. That is a signature problem, not an ownership problem.
    • You clone inside a loop, from outside it. Usually the value could be borrowed or hoisted.
    • You clone and then mutate the copy, and expect the original to change. Now it is a bug, not a cost.

    Yours sound like the first category is the risk, not performance.

    30
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report
  • @borrow_the_shape · 3h ago

    The habit that moved me past the clone-until-it-compiles stage: when a clone appears at a call site, look at the function signature rather than at the call.

    An enormous share of beginner clones are because a function takes an owned value when it only ever reads. Change the parameter to a borrow and the clone at every call site disappears at once. The pattern to internalise is to take the least you need — a borrow if you only read, and an owned value only if you genuinely keep or consume it.

    The related one for strings: take a string slice rather than an owned string when you only read it. That single change removes a large fraction of the clones in most first Rust programs.

    A useful exercise, and I would do this rather than a general clean-up: pick one clone that annoys you and try to remove it properly. Not by fighting, but by asking who should own this value. Usually the answer is that it should be owned further up and borrowed down, and restructuring that way makes three other clones unnecessary as well.

    Do that a few times and the borrow checker stops being an argument, because you start structuring things the way it expects before it complains. That is the thing you feel you are not learning, and it is learned exactly this way — one deliberate removal at a time, not by avoiding clone everywhere.

    26
    Share
    Reply

    Answering anonymously — a moderator will review it first.

    Report