---
title: "8.5 Copy types: when assignment copies"
description: "Why integers never triggered a move error: the Copy marker, which Rust types copy on assignment versus move, and how to tell at a glance."
url: "https://learnrust.net/chapter-8/copy-types/"
last_updated: "2026-06-12"
---

# 8.5 Copy types: when assignment copies

A fair objection has been building for two lessons. If assignment moves, how did seven chapters of code like this ever compile?

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

```
5 5
```

That's `x` used *after* `let y = x;`, the exact shape lesson [8.4](https://learnrust.net/chapter-8/moves/) just spent a page forbidding. No `E0382`, no drama, two usable variables. The compiler even told you why, in the error message you read line by line last lesson: `s1` moved because `String` "does not implement the `Copy` trait." Implication: some types *do*, and for them the story is different.

## Whole in the box

Think about what made copying a `String` on `=` unacceptable. Copying the handle made two owners of one heap block (double free); copying the block made assignment secretly expensive. Both problems come from the same place: a `String` is *more than its box*. The handle in the frame is just the visible part; the real cargo lives elsewhere, on the heap.

An `i32` has no elsewhere. The value 5 sits whole in its variable's box (lesson [1.3](https://learnrust.net/chapter-1/values-and-variables/): a name for a box in memory), four bytes, nothing attached. Copy the box and you have copied *everything*: a complete, independent, honest duplicate, at the cost of moving four bytes. Neither of the move-motivating problems can even be stated. Two boxes holding 5 share nothing, so no double free; and copying four bytes is as cheap as operations get, so no hidden expense.

For types like that, forcing the move rules would be all pain and no protection. So Rust marks them **Copy**, and for Copy types, `=` does the obvious thing chapters 1 through 7 always assumed: it copies, and *both* variables remain alive and independent.

(That word "trait" in the compiler's message is chapter 16's machinery for giving types capabilities; you don't need it yet. Read "implements `Copy`" as "carries the Copy marker.")

## The roster

Every type this course has taught, sorted:

| Type | On `let b = a;` |
|---|---|
| Integers: `i32`, `u64`, `usize`, all of chapter 4's roster | Copies |
| Floats: `f64`, `f32` | Copies |
| `bool` | Copies |
| `char` | Copies |
| The unit type `()` | Copies (there's nothing *to* copy) |
| Tuples, if every element is Copy: `(i32, f64)`, `(bool, char, u8)` | Copies |
| Tuples with any non-Copy element: `(i32, String)` | Moves |
| `&str` | Copies |
| `String` | Moves |

Two rows deserve a closer look.

**Tuples** are Copy exactly when all their parts are, which follows from the logic above: a tuple of Copy types lives whole in its boxes, so copying the boxes copies everything. Smuggle a `String` into one element, and the tuple inherits the `String`'s situation: part of its cargo is on the heap, so the whole tuple moves.

**`&str`** might look like the surprise, but lesson [5.4](https://learnrust.net/chapter-5/introduction-to-str/) did the work already. A `&str` is a *view*: it knows where some text starts and how long it is, and it owns nothing. Copying a view copies the where-and-how-long, not the text, like a second neighbor turning to watch the same bicycle through the same window. No ownership is duplicated because none was there. Views copy freely; *owners* move.

> **The one-question test**
>
> Does the type own heap memory (or any resource beyond its own bytes)? If no, it can be Copy, and the types you've met all are. If yes, it moves. You now know the deep reason `String` was introduced as Rust's *owned* text type: "owned" was this chapter's whole story, compressed into one adjective back in lesson [5.3](https://learnrust.net/chapter-5/introduction-to-string/).

## The first half of an old asterisk

Lesson [2.3](https://learnrust.net/chapter-2/parameters-and-arguments/) flagged its own simplification when it said arguments are copied into parameters: "the whole truth for integers and the other simple types in these chapters, and a simplification for types like `String`."

You can now cash half of that asterisk. For Copy types, "arguments are copied" was never a simplification at all; the argument's box is copied into the parameter's box in the called function's frame (lesson [8.2](https://learnrust.net/chapter-8/the-stack-and-the-heap/)), and both stay alive. That's the whole truth, no asterisk. What happens when the argument is a `String` is the other half, and it's next lesson's headline, though after 8.4 you can probably hear it coming.

## Quiz time

**Question #1**

Compile or not? One sentence of why.

```rust
fn main() {
    let flag = true;
    let copy = flag;
    println!("{flag} {copy}");
}
```

<details class="solution">
<summary>Show solution</summary>

Compiles, printing `true true`. `bool` is Copy: it lives whole in its box, so `=` duplicates it and both variables stay usable. This is every chapter 1–7 program, now with its license visible.

</details>

**Question #2**

Compile or not? One sentence of why.

```rust
fn main() {
    let label = (1, String::from("crate"));
    let moved = label;
    println!("{}", label.0);
}
```

<details class="solution">
<summary>Show solution</summary>

`error[E0382]`: the tuple contains a `String`, so the *whole tuple* moves to `moved`, and `label` can't be used afterwards, not even its innocent `i32` element. One non-Copy passenger makes the whole vehicle non-Copy.

</details>

**Question #3**

A colleague asks: "Why doesn't Rust just make `String` Copy too, so all this move business disappears?" Answer in two or three sentences, using the chapter's vocabulary.

<details class="solution">
<summary>Show solution</summary>

Copying a `String` on every `=` would mean either copying just the handle, which creates two owners of one heap block and a double free at the closing brace, or copying the heap block too, which makes a cheap-looking assignment secretly cost whatever the text weighs. Rust rejects both: silent danger and silent expense. Moves keep one owner per block (safety) and keep `=` cheap (honesty); when you genuinely want the expensive duplicate, lesson 8.7's `.clone()` lets you say so out loud.

</details>

Next lesson, the move rules meet functions, several chapter-5 debts get paid at once, and the compiler starts openly advertising chapter 9.

## Sitemap

See the full [sitemap](https://learnrust.net/sitemap.md) for all pages.
