Skip to main content

rust-clone-audit

Detect and eliminate abusive cloning in Rust: treat every `.clone()`, `.to_owned()`, `.to_vec()`, `Cow::into_owned()`, and equivalent owned-duplication call as a potential ownership-design defect, not a free fix for a borrow-checker error. Use when writing, reviewing, or refactoring Rust code, especially when a `.clone()` is added to make code compile.

Quellinformationen

Repository
cosmian/kms
Letzte Quellaktivität
10. Oktober 2026 um 21:50
Erkannte Sprache von SKILL.md
Englisch
Sterne
367
Forks
43

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
rust-clone-audit
description
Detect and eliminate abusive cloning in Rust: treat every `.clone()`, `.to_owned()`, `.to_vec()`, `Cow::into_owned()`, and equivalent owned-duplication call as a potential ownership-design defect, not a free fix for a borrow-checker error. Use when writing, reviewing, or refactoring Rust code, especially when a `.clone()` is added to make code compile.
# Rust Clone Audit Companion to [`rust-signature-design`](../rust-signature-design/SKILL.md) (parameter identity/count) and [`rust-refactor`](../rust-refactor/SKILL.md) (duplicated code blocks): this skill targets a different axis — the **owned-vs-borrowed decision for a single value** at the call site where a clone-equivalent call appears. It does not scan for duplicated logic or judge parameter lists. Goal: identify and aggressively eliminate unnecessary cloning. Must not introduce `.clone()` merely to satisfy the borrow checker without first exhausting borrowing, lifetime, and ownership-transfer alternatives — the same rule the lint-suppression decision tree in [`rust.instructions.md`](../../instructions/rust.instructions.md) already applies to `clippy::redundant_clone` and `clippy::clone_on_copy`. ## Step 1 — Classify every clone-equivalent call | Kind | Call shape | Legitimate when | KMS example | | --- | --- | --- | --- | | Handle clone | `Arc::clone(&x)`, `Rc::clone(&x)`, `x.clone()` on `Arc`/`Rc` | Always — O(1) ref-count bump, not a data copy | `Arc::clone(&recorder.calls)` (`database_objects.rs:1131`), `Arc::clone(&barrier)` (`audit/pgsql.rs:2070`) | | Thread/task move clone | `x.clone()` right before `tokio::spawn`/`thread::spawn(move \|\| ...)` | The spawned task outlives the current scope and needs its own owned copy | — | | Copy-type clone | `.clone()` on a `Copy` type (`u32`, `bool`, small enum) | Never needed — use the value directly or `.copied()`/`.cloned()` on an `Option`/`Iterator` | — | | Borrow-checker-dodge clone | `.clone()` added only to make an error disappear, with no owned-value requirement downstream | Never — restructure the borrow instead (see rust-unofficial/patterns, "Clone to satisfy the borrow checker") | — | | Field-extraction clone | `self.field.clone()` returned as an owned value from `&self` | The caller genuinely needs ownership (stores it, moves it across an `await`/thread boundary); otherwise return `&T`/`&str` | — | | API-forced clone | Function signature takes `T` but the body only reads it — every caller must `.clone()` to call it | Never by itself — change the parameter to `&T` (one signature change fixes every call site) | — | | Cow-eligible clone | `.to_string()`/`.to_owned()` used because *some* code paths modify the value and others don't | Only on the paths that actually modify; otherwise return `Cow<'_, str>` or the borrow | — | ## Step 2 — Discover ```bash # Clone-equivalent call density per file, worst-first rg -c '\.clone\(\)|\.to_owned\(\)|\.to_vec\(\)|Cow::into_owned\(\)' --type rust <path> | sort -t: -k2 -rn | head -20 # Clones immediately preceding a borrow-checker-shaped error pattern: same binding re-read right after rg -n '(\w+)\.clone\(\)' --type rust <path> -B1 | rg -B1 'let \w+ = &?\1\b' # Clones on Copy-eligible primitives (near-certain waste) rg -n '\.(u8|u16|u32|u64|usize|i8|i16|i32|i64|isize|bool|f32|f64)\(\)\.clone\(\)|: *(u8|u16|u32|u64|usize|bool)\b.*\.clone\(\)' --type rust <path> # Functions that take an owned T but only read it (API-forced clone candidates) rg -n 'fn \w+\([^)]*: (String|Vec<\w+>|HashMap<[^)]+>)[^)]*\)' --type rust <path> ``` ## Step 3 — Pick the narrowest fix (checklist, in order) 1. **Can the parameter/field be borrowed instead?** Change `T` → `&T` (or `&str`/`&[T]` for `String`/`Vec<T>`); update every caller — this is often the single-signature fix that removes N call-site clones at once. 2. **Is this `Arc::clone`/`Rc::clone`?** Stop — it is already correct, leave it. 3. **Is the value moving into a spawned task/thread/`'static` closure?** Clone is necessary; leave it, optionally add a `// moved into spawned task` comment if the reason isn't obvious from context. 4. **Does only some code paths need an owned value?** Return `Cow<'_, T>` instead of unconditionally cloning or unconditionally borrowing. 5. **Is the clone on a `Copy` type?** Remove `.clone()` entirely; use `.copied()`/`.cloned()` for `Option<&T>`/`Iterator<Item = &T>` conversions. 6. **Is the clone structurally required** (stored in a new owner, e.g. a struct field or a `HashMap` key/value, with no shared-ownership type in use)? Keep it — this is legitimate duplication, not abuse. Do not force `Arc`/`Rc` onto a type with no concurrent/shared ownership need just to avoid a single clone. ## Before / after (illustrative) ```rust // Before — caller forced to clone because the function takes ownership it never needs. fn log_message(msg: String) { println!("[LOG] {msg}"); } log_message(message.clone()); log_message(message); // After — borrow; zero allocation, callable repeatedly. fn log_message(msg: &str) { println!("[LOG] {msg}"); } log_message(&message); log_message(&message); ``` ## Do not - Do not remove a clone that is structurally required (the value is stored in a new owner, or moved into a spawned task/thread) just to reduce a clone count. - Do not replace a single clone with `Arc`/`Rc`/`RefCell` wrapping across a type with no concurrent or shared-ownership requirement — that trades one allocation for a bigger runtime cost (atomic ops, interior-mutability panics) and a wider API change. - Do not silence `clippy::redundant_clone` / `clippy::clone_on_copy` with a bare `#[allow]` instead of fixing the call site — forbidden on new code per `rust.instructions.md`. - Do not change a `crate/kmip` wire struct's field from owned to borrowed — KMIP structs are spec-bound value types serialized independently of caller lifetimes. - Do not flag `Arc::clone`/`Rc::clone` as defects — they are O(1) ref-count bumps, never a data copy (see Step 1). ## References - rust-unofficial/patterns, ["Clone to satisfy the borrow checker"](https://github.com/rust-unofficial/patterns/blob/main/src/anti_patterns/borrow_clone.md) - Microsoft, ["Avoiding Excessive clone()"](https://microsoft.github.io/RustTraining/c-cpp-book/ch17-1-avoiding-excessive-clone.html) — source of the Step 1 "legitimate when" column and the before/after example - Rust Project Goals, ["Ergonomic ref-counting"](https://goals.rust-lang.org/2024h2/ergonomic-rc.html)
Auf GitHub ansehen