Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: rust-ownership description: Stops coding agents from "fixing" Rust borrow-checker errors with .clone(), Arc, RefCell or Box::leak when the real fix is to restructure ownership. Use when writing or repairing Rust code, when rustc reports E0382, E0499, E0502, E0506, E0515 or E0597, when a borrow checker error must be resolved, or when reviewing Rust code for unnecessary clones and allocations. license: MIT metadata: author: KeWang0622 evidence: EVIDENCE.md
Rust ownership — do not clone your way out
When rustc rejects your code with a borrow or move error, it usually gives you nothing to act on: on 5 of the 6 cases in this repo's corpus it emits no help and no note at all. In the one case where it does offer a help, that help is "consider cloning the value".
So the path of least resistance is to clone until it compiles. Do not take it. Cloning to escape a borrow conflict does not fix the conflict — it makes two objects where the code assumed one.
The rule
Never introduce `.clone()`, `Arc`, `Rc`, `RefCell` or `Box::leak` for the purpose of silencing a compiler error. Those are designs, not fixes. Reach for them only when the design genuinely calls for shared or owned data, and say so explicitly.
When you hit a borrow error, work in this order:
- Shorten the borrow. Bind only what you need (a
bool, ausize, an owned
key) inside a tight scope so the borrow ends before the conflicting use.
- Change the signature. If a callee only reads, take
&T/&strinstead ofT. - Sequence the accesses. Do the read, finish with it, then do the write.
- Use the API built for it.
entry,split_at_mut, indices,std::mem::take. - Only then consider owning the data — and when you do, say in one sentence why
sharing or copying is the right design, not just what made the error go away.
Per-error playbook
| Code | What it means | Do NOT | Do | |
|---|---|---|---|---|
| E0382 | Value used after being moved | Pass x.clone() | Take &T in the callee | |
| E0499 | Two mutable borrows at once | Clone the collection | Sequence them, or split_at_mut | |
| E0502 | Mutable borrow while immutably borrowed | Clone the looked-up value | End the read in a tight scope, or use entry | |
| E0506 | Assign to a borrowed value | Clone the struct | Shorten the reference's lifetime | |
| E0515 | Return a reference to a local | Box::leak | Return the owned value | |
| E0597 | Borrow outlives its referent | Wrap in Rc<RefCell<_>> | Restructure so the value outlives the borrow |
The one that actually bites
E0499 and E0502 are the dangerous ones. Cloning there compiles cleanly, passes tests, and is silently wrong: the two halves of the code now mutate different objects, so writes through one are invisible to the other.
clippy::redundant_clone will not save you. It only fires when the original is never used again. In exactly these cases the original is still used — that is why the borrow conflicted — so clippy stays silent.
When cloning IS right
Cloning is not banned. It is correct when the data genuinely needs independent ownership: sending a value to another thread, storing a snapshot that must not change, or when profiling shows the copy is cheap and the restructure is not worth it. The test is whether you can state the reason without referring to the compiler error.
Evidence
Every claim above comes from compiling the programs in corpus/ with a real rustc and reading --error-format=json. See EVIDENCE.md, which is generated by node scripts/extract.mjs and re-verified in CI.