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.