---
title: "10.8 Ownership in structs"
description: "Structs own their fields, so moving a struct moves everything inside it; storing a reference in a struct requires a lifetime, which Rust defers to chapter 17."
url: "https://learnrust.net/chapter-10/ownership-in-structs/"
last_updated: "2026-06-13"
---

# 10.8 Ownership in structs

Structs don't escape chapters 8 and 9; they inherit them. A struct *owns its fields*, and that single fact decides how structs move, what happens when one is dropped, and why a tempting shortcut (storing a reference inside a struct) is the one thing this chapter won't let you do yet.

## A struct owns its fields

When a struct holds a `String`, the struct owns that `String`. The heap text belongs to the field, the field belongs to the struct, and the struct belongs to whoever owns the struct. Ownership nests, all the way down:

```rust
#[derive(Debug)]
struct User {
    name: String,
    age: u32,
}

fn main() {
    let user = User {
        name: String::from("Ada"),
        age: 36,
    };
    println!("{:?}", user);
}
```

```
User { name: "Ada", age: 36 }
```

`user` owns the `String` `"Ada"`. There's no separate owner for the text; it's part of the value. This is why lesson [10.1](https://learnrust.net/chapter-10/introduction-to-program-defined-types/) chose `String` fields rather than `&str` fields for `Customer`: an owning struct holds its own data and answers to nobody about how long that data lives.

When `user` goes out of scope at the end of `main`, it's dropped, and dropping it drops every field in turn: the `String`'s heap block is freed, the `u32` reclaims its bytes. The lesson [8.3](https://learnrust.net/chapter-8/scope-and-drop/) drop rule reaches into the struct automatically. You never free the fields yourself; owning the struct means owning the cleanup.

## Moving a struct moves its fields

Because the struct owns its fields, moving the struct moves the whole bundle, and the lesson [8.4](https://learnrust.net/chapter-8/moves/) move rule applies to the struct as a unit:

```rust
fn main() {
    let user = User { name: String::from("Ada"), age: 36 };
    let user2 = user;            // user moved into user2
    println!("{:?}", user);      // refused
}
```

```
error[E0382]: borrow of moved value: `user`
 --> src/main.rs:4:22
  |
2 |     let user = User { name: String::from("Ada"), age: 36 };
  |         ---- move occurs because `user` has type `User`, which does not implement the `Copy` trait
3 |     let user2 = user;
  |                 ---- value moved here
4 |     println!("{:?}", user);
  |                      ^^^^ value borrowed here after move
```

Same E0382, same three labels lesson [8.4](https://learnrust.net/chapter-8/moves/) taught you to read. The note says it plainly: `User` "does not implement the `Copy` trait," so `let user2 = user;` moves rather than copies. A struct is `Copy` only if *every* field is `Copy` (lesson [8.5](https://learnrust.net/chapter-8/copy-types/)), and the moment a struct contains a `String` it can't be, because the `String` can't be. A `Point { x: f64, y: f64 }` could be `Copy` (both fields are), and you'd opt in with `#[derive(Copy, Clone)]`; a struct with a `String` field never can.

The cures are the ones you already know. Borrow it (`let user2 = &user;`) if you only need to look. Clone it (`#[derive(Clone)]`, then `user.clone()`) if you genuinely need two independent owners and accept the copy cost (lesson [8.7](https://learnrust.net/chapter-8/clone/)). Pass `&User` to functions that read, `&mut User` to functions that change it, owned `User` to functions that keep it: the lesson [9.4](https://learnrust.net/chapter-9/references-and-functions/) table, unchanged, applied to your type.

> **Key insight**
>
> A struct doesn't get special ownership rules; it gets the *same* rules, applied to the whole at once. Move the struct, you move every field. Drop the struct, you drop every field. Ask whether the struct is `Copy`, and the answer is "only if all its fields are." Everything you learned about a lone `String` is true of a struct containing one, because the struct is just a labeled box around its fields.

## Why fields are owned, not borrowed

You might wonder why the `User` struct stores `name: String` (owned) instead of `name: &str` (borrowed). A borrowed field would avoid the copy. The reason is the one lesson [9.5](https://learnrust.net/chapter-9/dangling-references/) drilled: a reference must never outlive the value it points at. A struct that stored a `&str` would be storing a borrow, and that borrow would have to stay valid for as long as the struct lives. The compiler can't just take your word for that; it needs you to prove the borrowed-from data outlives the struct, and the tool for writing that proof is called a **lifetime**.

Try it and the compiler tells you exactly what's missing:

```rust
struct Excerpt {
    text: &str,
}
```

```
error[E0106]: missing lifetime specifier
 --> src/main.rs:2:11
  |
2 |     text: &str,
  |           ^ expected named lifetime parameter
  |
help: consider introducing a named lifetime parameter
  |
1 ~ struct Excerpt<'a> {
2 ~     text: &'a str,
  |
```

You've seen E0106 before, in lesson [9.5](https://learnrust.net/chapter-9/dangling-references/), when a function tried to return a reference to a local. It's the same machinery: the compiler wants you to name a lifetime (`'a`) so it can check that no `Excerpt` outlives the text it points into. That `<'a>` syntax is chapter 17's entire subject, and it's genuinely the next real layer of the type system, so we defer it deliberately rather than rush it here.

> **Best practice**
>
> Until chapter 17, give your structs *owned* fields: `String`, not `&str`; `Vec<T>`, not `&[T]`. An owning struct is self-contained, it can move and live anywhere, and it never drags a lifetime annotation into your code. Storing references in structs is a real and useful technique, but it's an intermediate one, and reaching for it before you've met lifetimes leads straight into E0106. Own your data; borrow at the function boundary.

This is the same advice lesson [5.4](https://learnrust.net/chapter-5/introduction-to-str/) gave for strings ("store `String`, pass `&str`"), now generalized to every type you'll define. A struct holds owned data; functions borrow it through `&self` and `&T` parameters. That division keeps lifetimes out of your structs and your structs free to go anywhere.

## Quiz time

**Question #1**

Does this compile? If not, name the error and give two different fixes.

```rust
#[derive(Debug)]
struct Note {
    body: String,
}

fn main() {
    let a = Note { body: String::from("hi") };
    let b = a;
    println!("{:?} {:?}", a, b);
}
```

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

It doesn't compile: E0382, use after move. `let b = a;` moves `a` (a `Note` containing a `String` can't be `Copy`), so reading `a` afterward is refused. Two fixes: (1) borrow, `let b = &a;`, since you only print; or (2) `#[derive(Clone)]` on `Note` and `let b = a.clone();` for two independent owners. Cloning copies the heap text; borrowing copies nothing.

</details>

**Question #2**

Why can a `Point { x: f64, y: f64 }` be made `Copy`, but a `User { name: String, age: u32 }` can never be?

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

A struct can be `Copy` only if every field is `Copy`. `f64` is `Copy`, so all of `Point`'s fields qualify and you can `#[derive(Copy, Clone)]`. `User` contains a `String`, which owns heap memory and is not `Copy` (lesson [8.5](https://learnrust.net/chapter-8/copy-types/)); one non-`Copy` field disqualifies the whole struct.

</details>

**Question #3**

What does E0106 ("missing lifetime specifier") on a struct field tell you, and what's the simplest way to avoid it for now?

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

It means you tried to store a reference (`&str`, `&T`) in a struct, and the compiler needs a lifetime annotation to guarantee the struct never outlives the borrowed data. The simplest fix until chapter 17 is to store an *owned* field instead: `String` rather than `&str`. Owned fields need no lifetimes.

</details>

That's everything a struct does on its own. The next lesson puts it all together in the chapter's project: the Rust Book's rectangle refactor, rebuilt one compiling step at a time, from loose variables to a tuple to a struct with methods.

## Sitemap

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