# Rust Ownership

> 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.

- Skill: `kewang0622/rust-ownership` (Agent Skill)
- Install (CLI): `npx skillmds@latest add kewang0622/rust-ownership`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kewang0622/rust-ownership/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT
- Author: kewang0622 (https://skillmd.com/u/kewang0622)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/kewang0622/rust-ownership

---


# 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:

1. **Shorten the borrow.** Bind only what you need (a `bool`, a `usize`, an owned
   key) inside a tight scope so the borrow ends before the conflicting use.
2. **Change the signature.** If a callee only reads, take `&T` / `&str` instead of `T`.
3. **Sequence the accesses.** Do the read, finish with it, then do the write.
4. **Use the API built for it.** `entry`, `split_at_mut`, indices, `std::mem::take`.
5. **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](../../EVIDENCE.md), which is
generated by `node scripts/extract.mjs` and re-verified in CI.

