# Ownership

> Explain how Rust frees memory without a garbage collector: owners, moves, Copy types, Clone, and Drop at end of scope.

Rust roadmap · Foundations · 17 min read · https://levelcode.org/roadmaps/rust/ownership

Ownership is the idea that makes Rust Rust. It's how the language frees memory at exactly the right moment, without a garbage collector and without you calling `free`. Three short rules produce it, and they also explain most of the errors you'll see in your first weeks. This lesson covers the stack and the heap, the rules, what a **move** is, which types are copied instead, how to duplicate data with `clone`, and how `Drop` runs cleanup code deterministically.

## The stack and the heap

Your program stores values in two places.

The **stack** holds values whose size is known at compile time: integers, floats, `bool`, `char`, fixed-size arrays, and structs made of them. Pushing and popping is just moving a pointer, so it's extremely fast, and a function's stack frame disappears when it returns.

The **heap** holds data whose size is only known at runtime or that must outlive the function that created it: a `String` that grows as you append to it, a `Vec` with a variable number of items. Allocating means asking the allocator for space and getting back a pointer. Someone has to give that space back exactly once, and never touch it afterwards.

A `String` is both. The value on the stack is three words: a pointer to a heap buffer, a length and a capacity. The characters live in the heap buffer.

```text
 stack (s)                 heap
┌──────────┬───────┐      ┌───┬───┬───┬───┬───┐
│ ptr      │  ─────┼────► │ h │ e │ l │ l │ o │
│ len      │   5   │      └───┴───┴───┴───┴───┘
│ capacity │   5   │
└──────────┴───────┘
```

Every language has to decide who frees that buffer and when. C makes you do it, and bugs are freeing it twice, freeing it too early (use-after-free), or never (a leak). Garbage-collected languages track reachability at runtime and free it some time later. Rust decides at compile time, using ownership.

## The three rules

1. Each value has exactly one **owner**: a variable, a struct field, a collection slot.
2. There can be only one owner at a time.
3. When the owner goes out of scope, the value is **dropped**: its destructor runs and its memory is freed.

```rust
fn main() {
    {
        let s = String::from("hello"); // s owns the heap buffer
        println!("{s}");
    } // s goes out of scope: the buffer is freed here, automatically
}
```

That closing brace is where Rust inserts the call to free the buffer. There's no runtime tracking and no pause. It's the same code you'd write by hand in C, except the compiler writes it and can't forget it.

## Moves

What happens when you assign a `String` to another variable?

```rust
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;
    println!("{s1}"); // error
}
```

```text
error[E0382]: borrow of moved value: `s1`
 --> src/main.rs:4:15
  |
2 |     let s1 = String::from("hello");
  |         -- move occurs because `s1` has type `String`, which does not implement the `Copy` trait
3 |     let s2 = s1;
  |              -- value moved here
4 |     println!("{s1}");
  |               ^^^^ value borrowed here after move
```

`let s2 = s1` copies the three stack words (pointer, length, capacity) into `s2`. Now two variables point at the same heap buffer. If both were owners, both would free it at the end of scope: a double free. Rust's answer is that ownership **moves** to `s2`, and `s1` becomes unusable. The compiler enforces it, so there's no runtime cost: no reference count, no flag.

Moves happen everywhere a value is handed over, not just in `let`:

```rust
fn takes(s: String) {
    println!("got {s}");
} // s is dropped here

fn gives() -> String {
    String::from("yours") // ownership moves out to the caller
}

fn main() {
    let a = String::from("mine");
    takes(a);            // a moves into the function
    // println!("{a}");  // error: a was moved

    let b = gives();     // b now owns the returned String
    println!("{b}");
}
```

Passing a value to a function moves it. Returning a value moves it out. Pushing it into a `Vec` moves it into the vector. Once you see the pattern, the errors make sense: *you gave it away; you can't use it anymore.*

Most of the time you don't want to give a value away, just let a function look at it. That's a **reference**, and it's the next lesson, [references and borrowing](https://levelcode.org/roadmaps/rust/borrowing).

## Copy types

Integers don't behave this way:

```rust
fn main() {
    let x = 5;
    let y = x;
    println!("{x} {y}"); // fine
}
```

`i32` implements the **`Copy`** trait. A `Copy` type lives entirely on the stack, so copying its bits produces a fully independent value and there's nothing to double-free. Assigning or passing it copies instead of moving.

`Copy` types include all integer and float types, `bool`, `char`, shared references `&T`, and tuples and arrays made only of `Copy` types. `String`, `Vec<T>`, `Box<T>` and anything else that owns heap memory or another resource can never be `Copy`.

You can make your own small types `Copy`:

```rust
#[derive(Debug, Clone, Copy)]
struct Point {
    x: f64,
    y: f64,
}

fn main() {
    let a = Point { x: 1.0, y: 2.0 };
    let b = a;               // copied, not moved
    println!("{a:?} {b:?}");
}
```

> [!NOTE]
> `Copy` requires `Clone`, which is why both appear in the derive. Derive `Copy` for small, plain-data types where an implicit copy is cheap and unsurprising. Don't add it to a type that might later own a `String` or `Vec`: removing `Copy` later breaks every caller that relied on it.

## Clone: an explicit deep copy

When you really need two independent owners of heap data, ask for a copy with `.clone()`:

```rust
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone(); // allocates a new buffer and copies the bytes
    println!("{s1} {s2}");
}
```

`clone` is explicit on purpose: it can be expensive (a `Vec` of a million items copies a million items), and Rust wants that cost visible in the code. Implicit copies are reserved for `Copy` types, which are always cheap.

Beginners often scatter `.clone()` to make the borrow checker go quiet. Sometimes that's fine; cloning a short string at startup costs nothing that matters. But inside a hot loop, or when cloning a large structure just to read it, it's a sign you wanted a reference. Profile before worrying, and prefer borrowing by default.

## Drop and RAII

When a value's owner goes out of scope, Rust calls its **destructor**, defined by the `Drop` trait. For `String` and `Vec`, it frees the heap buffer. For a file, it closes the file descriptor. For a mutex guard, it unlocks the mutex. This pattern, tying a resource's lifetime to an owner's scope, is called **RAII** (Resource Acquisition Is Initialization), borrowed from C++.

You can implement `Drop` yourself to see exactly when it runs:

```rust
struct Noisy(&'static str);

impl Drop for Noisy {
    fn drop(&mut self) {
        println!("drop {}", self.0);
    }
}

fn main() {
    let _a = Noisy("a");
    {
        let _b = Noisy("b");
        println!("inner scope ends");
    }
    let c = Noisy("c");
    drop(c); // drop early, explicitly
    println!("main ends");
}
```

```text
inner scope ends
drop b
drop c
main ends
drop a
```

Values are dropped in reverse order of declaration at the end of their scope. `std::mem::drop` (in the prelude as `drop`) takes ownership of a value and lets it go immediately, which is useful to release a lock or close a file before the end of a long function.

Because drop is deterministic, you don't need `defer` (Go), `try/finally` (Java) or `using` (C#). Files close, locks release and connections return to their pool when their owner's scope ends, even on early returns and panics.

> [!IMPORTANT]
> A value is dropped once, by its current owner. After a move, the old variable is not dropped; the compiler knows it no longer owns anything. That's how moves and drops together rule out double frees.

## Ownership and structs

Ownership composes. A struct owns its fields, a `Vec` owns its elements, and dropping the outer value drops everything inside it, recursively.

```rust
struct User {
    name: String,
    tags: Vec<String>,
}

fn main() {
    let u = User { name: String::from("ada"), tags: vec![String::from("admin")] };
    let name = u.name;        // moves just the name field out of u
    println!("{name}");
    println!("{:?}", u.tags); // fine: tags wasn't moved
    // println!("{}", u.name); // error: u.name was moved
}
```

This is a **partial move**: you can move individual fields out of a struct you own, and still use the fields you didn't move. You can't use `u` as a whole afterwards.

## Practice

Predict, then check with `cargo check`.

1. Write a function `fn shout(s: String) -> String` that returns `s` in upper case (`s.to_uppercase()`). In `main`, create a `String`, call `shout`, and print both the original and the result. Fix the error **without** using `clone` by changing what `main` does with the return value.
2. Create `let v = vec![String::from("a"), String::from("b")];` and loop over it with `for s in v { ... }`. After the loop, try to print `v.len()`. Read the error: what did the `for` loop do to `v`?
3. Implement `Drop` for a struct `Guard(String)` that prints `"releasing <name>"`. Create three guards in `main`, drop the second one explicitly halfway through, and predict the output order before running it.

**Solution**

1. Calling `shout(s)` moves `s` into the function, so printing `s` afterwards fails. Since `shout` already returns a `String`, take the result back instead of keeping the original:

```rust
fn shout(s: String) -> String {
    s.to_uppercase()
}

fn main() {
    let s = String::from("hello");
    let s = shout(s); // move in, get a new String back
    println!("{s}");  // HELLO
}
```

A version that keeps the original and the result would take `&str` instead. That's next lesson.

2. `for s in v` calls `v.into_iter()`, which consumes the vector: each `String` moves out into `s`. After the loop, `v` has been moved:

```text
error[E0382]: borrow of moved value: `v`
help: consider iterating over a slice of the `Vec<String>`'s content to avoid moving into the `for` loop
  |
  |     for s in &v {
  |              +
```

`for s in &v` borrows each element instead.

3.

```rust
struct Guard(String);

impl Drop for Guard {
    fn drop(&mut self) {
        println!("releasing {}", self.0);
    }
}

fn main() {
    let _one = Guard("one".into());
    let two = Guard("two".into());
    let _three = Guard("three".into());
    drop(two);
    println!("end of main");
}
```

```text
releasing two
end of main
releasing three
releasing one
```

`two` is dropped where you call `drop`. The rest are dropped in reverse declaration order when `main` ends.

Note the leading underscore in `_one`: it tells the compiler the variable is intentionally unused, but the value still lives until the end of scope. A bare `_` (as in `let _ = Guard(...)`) is different: it doesn't bind the value at all, so it's dropped immediately.

## Key takeaways

- Every value has one owner; when the owner goes out of scope, the value is dropped and its memory freed, at compile-time-determined points.
- Assigning, passing or returning a non-`Copy` value **moves** it; the old binding can't be used afterwards.
- `Copy` types (integers, floats, `bool`, `char`, `&T`, small plain-data structs) are copied bit-for-bit instead of moved.
- `.clone()` is an explicit, possibly expensive, deep copy. Prefer borrowing; clone when you truly need a second owner.
- `Drop` runs deterministically at end of scope, in reverse declaration order, which is how files, locks and connections clean themselves up.

## Quiz

1. Why does Rust move a String on assignment instead of copying it?

   A. Copying strings is impossible in Rust
   B. Strings are stored on the stack
   C. Moving is required by the garbage collector
   D. Two owners of the same heap buffer would both free it

   **Answer: D.** A String's stack part is a pointer to a heap buffer. Copying the pointer would create two owners that each free the buffer when dropped. A move transfers ownership so exactly one drop happens.

2. Which of these types is Copy?

   A. (i32, bool, char)
   B. Vec<u8>
   C. String
   D. Box<i32>

   **Answer: A.** A tuple of Copy types is Copy. Vec, String and Box all own heap memory, so they can only be moved or explicitly cloned.

3. fn f(v: Vec<i32>) {} … let v = vec![1, 2]; f(v); println!("{:?}", v); — what happens?

   A. Prints [1, 2]
   B. Compile error: v was moved into f
   C. Prints [] because f emptied it
   D. Runtime panic: use after free

   **Answer: B.** Passing v by value moves it into f, and the Vec is dropped when f returns. The compiler rejects the later use at compile time; it can never become a runtime use-after-free.

4. Three values a, b, c are declared in that order in one scope, and none is moved. In which order are they dropped?

   A. a, b, c
   B. Unspecified; it depends on the allocator
   C. c, b, a
   D. Whenever the garbage collector runs

   **Answer: C.** Locals are dropped in reverse order of declaration at the end of their scope. That's deterministic, so later values (which may depend on earlier ones) are cleaned up first.
