# Rust Syntax Ownership

> Use when the user writes Rust code that triggers ownership-transfer compile errors, asks "why moved value", asks to choose between Copy / Clone / Drop, encounters E0382 / E0507 / E0509 / E0382, or needs to design APIs around ownership transfer. Prevents excessive cloning, accidental moves, Copy+Drop mistakes, partial-move confusion, and ownership patterns that defeat the borrow checker. Covers: the three ownership rules, move semantics (by-value transfer on assignment / fn arg / return), Copy trait (which types qualify, why String / Vec / Box do not), Clone trait, scope-based drop and explicit `drop()`, ownership transfer patterns (builder, into-impl, by-value method receivers), partial moves out of fields. Keywords: ownership, move, moved value, "use of moved value", E0382, E0507, E0509, "cannot move out of", Copy, Clone, Drop, "drop function", scope, "ownership transfer", "by value", partial move, "move out of borrowed content", builder pattern, into method, "do I need to clone", "why can't I use this".

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

---


# rust-syntax-ownership

The **mechanics** of ownership transfer in Rust: how moves work, when `Copy` applies, when to call `.clone()`, when `Drop` runs, and the API patterns that consume `self` by value (builder, `From`/`Into`, `into_inner`). Partial moves out of struct fields are covered here too.

Cross-references: [[rust-core-memory-model]] (conceptual overview, big picture) [[rust-syntax-borrowing]] (the alternative to move) [[rust-errors-borrow-checker]] (E-codes in depth)

---

## When to use this skill

- User writes code that hits a move-related compile error: E0382, E0507, E0509, E0040, E0184, E0204, E0515
- User asks "why was this value moved" or "why can't I use `x` after passing it to a function"
- User wonders whether to derive `Copy`, derive `Clone`, both, or neither
- User wants to design a fluent builder, an `Into<T>` conversion, or an `into_inner` accessor
- User struggles with partial moves out of struct fields (`s.field` moved, but `s` still partly usable)
- User asks "do I need to clone here" or wants to remove `.clone()` calls that Clippy flagged

For the borrow alternative (sharing without taking ownership) see [[rust-syntax-borrowing]]. For the conceptual story (stack vs heap, smart pointers, Send/Sync) see [[rust-core-memory-model]].

---

## The three ownership rules (verbatim from the Book)

1. **Each value in Rust has an _owner_.**
2. **There can only be one owner at a time.**
3. **When the owner goes out of scope, the value will be dropped.**

ALWAYS state these three rules first when explaining any ownership scenario. Every other rule in this skill is a consequence of them.

[Source: Rust Book Ch 4.1 - What is Ownership?][book-own]

---

## Quick reference table

| Situation | What happens | Compiler behaviour |
|-----------|--------------|--------------------|
| `let b = a;` and `a: String` | `a` is moved into `b` | Using `a` afterwards: E0382 |
| `let b = a;` and `a: i32` | `a` is copied into `b` | Both `a` and `b` remain valid |
| `fn take(s: String)` called with `take(my_string)` | `my_string` moved into the function | Caller's `my_string` invalidated |
| `fn take(s: String) -> String { s }` | Ownership returned to caller | Receiver of return value owns it |
| `let s = my_struct.field;` (non-`Copy` field) | Partial move out of `my_struct` | `my_struct` cannot be used as a whole; other fields still usable |
| `drop(value)` | Calls `Drop::drop` immediately, then frees | `value` invalidated after the call |
| `value.drop()` | NEVER allowed; method is private | E0040 |
| Owner goes out of scope | `Drop::drop` runs, then memory is freed | Automatic, no syntax |

---

## Move semantics

Assigning a non-`Copy` value moves ownership. The original binding is **invalidated at compile time**:

```rust
let s1 = String::from("hi");
let s2 = s1;                 // s1 moved into s2
// println!("{s1}");         // E0382: value used after move
println!("{s2}");            // ok
```

The same rule applies to:

- **Function arguments**: `fn take(s: String)` consumes the caller's value.
- **Return values**: `fn give() -> String { String::from("hi") }` moves the value to the caller.
- **Destructuring**: `let (a, b) = pair;` where `pair: (String, String)` moves both halves; `pair` is invalidated.
- **Closure captures**: `move || println!("{s}")` moves `s` into the closure.

ALWAYS think of a binding as the **owner**, not as the storage cell. The value is moved; the heap payload (if any) is now reachable only through the new owner.

NEVER expect a moved binding to be usable. The compiler tracks moves statically; even an `if`-only move makes the binding unconditionally invalid on the other path unless the value is re-initialised.

[Source: Rust Book Ch 4.1 - What is Ownership?][book-own]

---

## The Copy trait

`Copy` makes assignment a **bit-by-bit duplication** rather than a move. `Copy` is **implicit, never overloadable**:

```rust
let x: i32 = 5;
let y = x;            // copy (i32 is Copy)
println!("{x} {y}");  // both valid
```

### Rules for Copy

- A type can implement `Copy` **only if all its fields are `Copy`**.
- `Copy` and `Drop` are **mutually exclusive**. A type that implements `Drop` can never be `Copy` (E0184).
- `Copy` **requires `Clone`**. Every `Copy` type must also derive or implement `Clone` (E0204 if missing).
- `#[derive(Copy, Clone)]` works only when all fields are themselves `Copy`. If any field is `String`, `Vec`, `Box`, `Rc`, `Arc`, etc., the derive fails.

### Which types are Copy

| Always Copy | Never Copy |
|-------------|------------|
| All integers: `i8` ... `u128`, `isize`, `usize` | `String` (owns heap buffer) |
| Floats: `f32`, `f64` | `Vec<T>` (owns heap buffer) |
| `bool`, `char` | `Box<T>` (owns heap allocation) |
| The never type `!` | `Rc<T>`, `Arc<T>` (own refcount) |
| Shared references `&T` for any `T` | Anything implementing `Drop` |
| Raw pointers `*const T`, `*mut T` | Tuples / arrays containing non-`Copy` elements |
| Function pointers `fn(...) -> _` | Mutable references `&mut T` |
| Function items (each `fn`'s anonymous type) | Most user-defined types unless explicitly derived |
| Tuples / arrays where **every** element is `Copy` | |

ALWAYS check field-by-field before deriving `Copy`. NEVER derive `Copy` on a type owning heap data.

[Source: std::marker::Copy][copy-trait]

---

## The Clone trait

`Clone` is **explicit and arbitrary**. Calling `.clone()` runs user-defined code:

```rust
let a = String::from("hi");
let b = a.clone();        // deep copy: heap buffer duplicated
println!("{a} {b}");      // both valid
```

### Rules for Clone

- `Clone` is a **supertrait** of `Copy`. Every `Copy` type also implements `Clone`.
- For a `Copy` type, `clone()` can be (and typically is, when derived) implemented as `*self`.
- `#[derive(Clone)]` requires all fields to be `Clone`.
- A type can implement `Clone` without being `Copy` (the common case for heap-owning types).

### Clone-but-not-Copy types

- `String`, `Vec<T>`, `Box<T>`, `HashMap<K, V>`, `BTreeMap<K, V>`, `Rc<T>`, `Arc<T>` are all `Clone`.
- `Rc::clone(&rc)` and `Arc::clone(&arc)` bump the refcount; they do NOT deep-copy the inner `T`.
- `Vec<T>` requires `T: Clone` to derive `Clone`.

ALWAYS prefer `Rc::clone(&rc)` / `Arc::clone(&arc)` over `rc.clone()` in code that should make the cheap refcount-bump intent explicit. Both compile to identical code.

NEVER use `.clone()` to silence the borrow checker. Borrow first; clone only when you genuinely need two independent owners (Clippy lint: `clippy::redundant_clone`).

[Source: std::clone::Clone][clone-trait]

---

## Decision tree: move vs borrow vs clone

```
Need to pass a value to a function or another binding?
|
+-- Will the original be used again afterwards?
|   |
|   +-- NO  -> move (just pass it; ownership transfers cleanly)
|   |
|   +-- YES -> borrow (& for read, &mut for write)
|              See [[rust-syntax-borrowing]] for details.
|
+-- The borrow checker rejected your borrow attempt?
    |
    +-- Step 1: restructure (scope the borrow shorter, split the data,
    |           return an owned value, take ownership by value).
    +-- Step 2: ONLY clone when:
    |           - The type is cheap to clone (Copy-like, Arc<T>, small Box).
    |           - OR you genuinely need two independent owners.
    +-- NEVER clone "just to make it compile". This is the
        single most common Rust anti-pattern (Clippy: redundant_clone).
```

ALWAYS try borrowing before cloning. ALWAYS try restructuring before cloning. Cloning is the third choice, not the first.

---

## Drop and the explicit `drop()` function

`Drop` runs **automatically** when an owner goes out of scope. This is Rust's **RAII** guarantee:

```rust
pub trait Drop {
    fn drop(&mut self);
}
```

### Critical rules

- You **cannot** call `.drop()` directly on a value (E0040: "explicit use of destructor method"). The method is implicitly private at the call site.
- To drop early, use the **free function** `std::mem::drop`, which is in the prelude as `drop`:
  ```rust
  let s = String::from("hi");
  drop(s);             // calls Drop::drop, then `s` is gone
  // println!("{s}");  // E0382: value used after move
  ```
  `drop()` is literally just `pub fn drop<T>(_x: T) {}`. It takes its argument by value, so the move-and-go-out-of-scope rule does the rest.
- `Drop::drop` runs **even during panic unwinding**. This is what makes `Mutex` poisoning, `File` close, and `MutexGuard` release correct.
- `Drop::drop` itself SHOULD NOT panic. A panic while unwinding from another panic becomes a double-panic and aborts the process.

### Drop order

- **Local variables** in a block: dropped in **reverse declaration order** (LIFO).
- **Struct fields, tuple elements, array elements**: dropped in **declaration order** (first-to-last).
- **Enum variants**: the active variant's fields drop in declaration order.

```rust
struct Loud(&'static str);
impl Drop for Loud {
    fn drop(&mut self) { println!("drop {}", self.0); }
}

fn demo() {
    let a = Loud("a");   // declared first  -> dropped last
    let b = Loud("b");   // declared second -> dropped first
}
// Output:
// drop b
// drop a
```

ALWAYS rely on `Drop` for resource cleanup. NEVER design a type that requires the user to call a manual `.close()` method.

[Source: std::mem::drop][drop-fn] [Source: std::ops::Drop][drop-trait]

---

## Ownership transfer patterns

### Pattern A: Builder consuming `self`

A builder takes `self` by value, mutates, and returns `Self`. Each call moves the builder forward:

```rust
pub struct Request {
    method: String,
    url: String,
    headers: Vec<(String, String)>,
}

impl Request {
    pub fn new(url: impl Into<String>) -> Self {
        Self { method: "GET".into(), url: url.into(), headers: vec![] }
    }

    pub fn method(mut self, m: impl Into<String>) -> Self {
        self.method = m.into();
        self
    }

    pub fn header(mut self, k: impl Into<String>, v: impl Into<String>) -> Self {
        self.headers.push((k.into(), v.into()));
        self
    }

    pub fn build(self) -> Request { self }
}

let r = Request::new("https://example.com")
    .method("POST")
    .header("accept", "application/json")
    .build();
```

ALWAYS prefer `mut self -> Self` builders over `&mut self -> &mut Self` builders when each step is logically a fresh value, because the consuming form chains cleanly into a final `build()` that returns an owned `Self`.

### Pattern B: `From` / `Into` for conversions

Implement `From<X> for Y` and you get `Into<Y> for X` for free via the blanket impl:

```rust
pub struct Celsius(pub f64);
pub struct Fahrenheit(pub f64);

impl From<Celsius> for Fahrenheit {
    fn from(c: Celsius) -> Self { Fahrenheit(c.0 * 9.0 / 5.0 + 32.0) }
}

let f: Fahrenheit = Celsius(100.0).into();   // uses the From impl
let f2 = Fahrenheit::from(Celsius(0.0));     // equivalent
```

ALWAYS implement `From` rather than `Into` directly; `Into` is then automatic. NEVER implement both `From<X> for Y` and `Into<Y> for X` by hand; they will conflict with the blanket impl.

For conversions that can fail, use `TryFrom` / `TryInto`.

### Pattern C: By-value method receiver (`fn into_inner(self)`)

A method that **consumes** the receiver returns owned inner data:

```rust
pub struct Wrapper<T> { inner: T }

impl<T> Wrapper<T> {
    pub fn into_inner(self) -> T { self.inner }
}

let w = Wrapper { inner: String::from("hi") };
let s: String = w.into_inner();   // w is consumed
// w used here would be E0382
```

ALWAYS name an `into_*` method when it takes `self` by value and returns ownership. NEVER call such a method `as_*` (that name is reserved for cheap borrow conversions) and NEVER call it `to_*` (reserved for explicit owned conversions that do not consume `self`).

[Source: Rust API Guidelines - as/to/into][api-guidelines]

---

## Partial moves out of fields

Moving a non-`Copy` field out of a struct partially invalidates the struct:

```rust
struct Pair { a: String, b: String }
let p = Pair { a: "A".into(), b: "B".into() };

let a = p.a;             // p.a moved out; p is now partly invalid
// let whole = p;        // E0382: use of partially moved value `p`
println!("{}", p.b);     // ok: p.b is still owned by p
drop(a);                 // ok
```

After a partial move:

- The struct as a whole **cannot** be used (no `let q = p;`, no `&p`, no passing by value).
- Other fields are **still usable individually** by name (`p.b`).
- The remaining fields will still be dropped when `p` goes out of scope.

### Fixing partial-move blockers

When you need the field back **and** still want the whole struct usable, use `mem::take` or `mem::replace`:

```rust
use std::mem;

struct Buffer { data: String }
let mut buf = Buffer { data: "payload".into() };

// Take the data out, leaving String::default() ("") behind.
let owned: String = mem::take(&mut buf.data);
// buf is still fully usable; buf.data is now "".

// Or swap a replacement in:
let prev: String = mem::replace(&mut buf.data, String::from("next"));
// buf.data is now "next"; `prev` holds "payload".
```

- `mem::take(&mut x)` returns the value at `x` and leaves `T::default()` in its place. Requires `T: Default`.
- `mem::replace(&mut x, new)` returns the old value at `x` and stores `new` there. Does NOT require `T: Default`.

ALWAYS use `mem::take` / `mem::replace` to extract owned data from behind a `&mut` reference. NEVER attempt to move out of `&self` or `&mut self` directly: the compiler rejects it (E0507, E0509).

[Source: std::mem::take][mem-take] [Source: std::mem::replace][mem-replace]

---

## Common error codes

| Code | Trigger | Fix |
|------|---------|-----|
| E0382 | "use of moved value" : a binding was moved, then used | Clone before the move, restructure to borrow, or do not use afterwards |
| E0507 | "cannot move out of borrowed content" : moving through `&` or `&mut` | Use `mem::take` / `mem::replace`, clone, or redesign to take by value |
| E0509 | "cannot move out of type ... which implements `Drop`" | Same as E0507; `Drop` types cannot be partially moved out of |
| E0040 | "explicit use of destructor method" : called `.drop()` directly | Use the free function `drop(value)` |
| E0184 | "the trait `Copy` may not be implemented for this type ... has a destructor" | Remove `Copy` or remove `Drop`; they are mutually exclusive |
| E0204 | "the trait `Copy` cannot be implemented for this type" : a field is not `Copy` | Remove `#[derive(Copy)]`; keep `Clone` |
| E0515 | "cannot return reference to local variable" | Return an owned value instead of a reference |

Full E-code drilldown lives in [[rust-errors-borrow-checker]].

---

## Section: Avoid these mistakes

(Full list with WHYs in `references/anti-patterns.md`.)

- Sprinkling `.clone()` everywhere to silence the borrow checker (Clippy `redundant_clone`).
- Deriving `#[derive(Copy, Clone)]` on a struct containing `String`, `Vec`, `Box`, `Rc`, or `Arc` (E0204).
- Implementing `Drop` on a type derived as `Copy` (E0184).
- Calling `value.drop()` instead of `drop(value)` (E0040).
- Trying to move a field out of `&self` or `&mut self` (E0507) without `mem::take` / `mem::replace`.
- Returning a reference to a function-local value (E0515: "cannot return reference to local variable").
- Re-using a binding after it was moved into a function or another binding (E0382).
- Treating a partial move as "the whole struct is broken" : the individual unmoved fields are still usable.

---

## Reference links

[book-own]: https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html
[copy-trait]: https://doc.rust-lang.org/std/marker/trait.Copy.html
[clone-trait]: https://doc.rust-lang.org/std/clone/trait.Clone.html
[drop-trait]: https://doc.rust-lang.org/std/ops/trait.Drop.html
[drop-fn]: https://doc.rust-lang.org/std/mem/fn.drop.html
[mem-take]: https://doc.rust-lang.org/std/mem/fn.take.html
[mem-replace]: https://doc.rust-lang.org/std/mem/fn.replace.html
[api-guidelines]: https://rust-lang.github.io/api-guidelines/naming.html
[from-trait]: https://doc.rust-lang.org/std/convert/trait.From.html
[into-trait]: https://doc.rust-lang.org/std/convert/trait.Into.html
[special-types]: https://doc.rust-lang.org/reference/special-types-and-traits.html
[redundant-clone]: https://rust-lang.github.io/rust-clippy/master/index.html#/redundant_clone

- [Rust Book Ch 4.1: What is Ownership?][book-own]
- [`std::marker::Copy`][copy-trait]
- [`std::clone::Clone`][clone-trait]
- [`std::ops::Drop`][drop-trait]
- [`std::mem::drop`][drop-fn]
- [`std::mem::take`][mem-take]
- [`std::mem::replace`][mem-replace]
- [`std::convert::From`][from-trait] / [`std::convert::Into`][into-trait]
- [Rust Reference: Special Types and Traits][special-types]
- [Rust API Guidelines : naming][api-guidelines]
- [Clippy lint: redundant_clone][redundant-clone]

For deeper drill-downs see:
- `references/methods.md`: trait signatures (Copy, Clone, Drop, From, Into) and free-function signatures for `mem`.
- `references/examples.md`: complete working examples for moves, partial moves, builders, conversions, by-value receivers.
- `references/anti-patterns.md`: the most common ownership mistakes with root-cause explanations and fixes.

