---
title: "17.3 Lifetimes in structs"
description: "How a struct can hold a reference by declaring a lifetime parameter, what the annotation guarantees, and why owned fields remain the simpler default."
url: "https://learnrust.net/chapter-17/lifetimes-in-structs/"
last_updated: "2026-06-13"
---

# 17.3 Lifetimes in structs

Lesson [10.8](https://learnrust.net/chapter-10/ownership-in-structs/) hit a wall: trying to put a `&str` in a struct produced E0106, "missing lifetime specifier," and the advice was "use owned fields until chapter 17." This is chapter 17, and the IOU comes due. A struct *can* hold a reference, you just have to declare a lifetime parameter, and this lesson shows how, what it guarantees, and why owned fields are still usually the better call.

## The problem, revisited

Here's the struct that failed in lesson [10.8](https://learnrust.net/chapter-10/ownership-in-structs/):

```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,
  |
```

The compiler's worry is the dangling-reference rule (lesson [9.5](https://learnrust.net/chapter-9/dangling-references/)) applied to storage. If an `Excerpt` holds a `&str`, that borrowed text must stay alive for at least as long as the `Excerpt` does, or the struct would outlive its own data and hold a dangling reference. The compiler can't just assume that relationship; it needs you to state it with a lifetime parameter.

## Declaring a lifetime on a struct

The fix is the one the compiler suggested: give the struct a lifetime parameter, declared in angle brackets after the name (just like a generic type parameter, chapter 15), and attach it to the reference field:

```rust
struct Excerpt<'a> {
    text: &'a str,
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("no sentence");

    let excerpt = Excerpt { text: first_sentence };
    println!("{}", excerpt.text);
}
```

```
Call me Ishmael
```

`struct Excerpt<'a>` reads "an `Excerpt` is tied to some lifetime `'a`," and `text: &'a str` says "the `text` field borrows for that lifetime." Together they declare the rule the compiler wanted: **an `Excerpt` cannot outlive the text it borrows.** In `main`, `excerpt` borrows from `novel`, and because `novel` lives for the rest of `main`, the borrow is valid, so it compiles. If `novel` were dropped while `excerpt` still lived, the borrow checker would refuse, exactly as it would for a plain reference that outlived its value.

The annotation does the same job it did for functions (lesson [17.2](https://learnrust.net/chapter-17/lifetime-annotations-in-functions/)): it *describes* the relationship (the struct borrows, and is bounded by the borrow's lifetime) so the compiler can enforce it. It doesn't extend anything.

## Methods on a struct with a lifetime

When you write `impl` blocks for such a struct, the lifetime parameter comes along, just like a generic parameter does (lesson [15.4](https://learnrust.net/chapter-15/generic-methods/)):

```rust
impl<'a> Excerpt<'a> {
    fn announce(&self, note: &str) -> &str {
        println!("Attention: {note}");
        self.text
    }
}
```

`impl<'a> Excerpt<'a>` declares the lifetime for the impl block, mirroring `impl<T> Point<T>` from chapter 15. The method bodies mostly don't need extra lifetime annotations thanks to elision (the next lesson), which is why `announce` returns `&str` without a written lifetime. The pattern to recognize: a struct holding a reference threads its lifetime parameter through its definition and its `impl` blocks, the same way a generic struct threads its type parameter.

> **Key insight**
>
> A lifetime on a struct ties the struct's validity to its borrowed data: the struct may not outlive what it borrows. This is the borrow checker's "references must be valid" rule (lesson [9.5](https://learnrust.net/chapter-9/dangling-references/)) extended to data *stored* inside a type, not just passed through a function. The annotation makes that relationship explicit so the compiler can check every place an `Excerpt` is created and used, refusing any where the borrowed text would die first.

## Why owned fields are still the default

You can now store references in structs, but lesson [10.8](https://learnrust.net/chapter-10/ownership-in-structs/)'s advice still stands: prefer owned fields (`String`, not `&str`) unless you have a specific reason not to. A struct with a lifetime parameter is *constrained*: it can't outlive its borrowed data, which means it can't be returned from a function that created the data locally, can't be stored just anywhere, and threads `<'a>` through every signature that mentions it. An owning struct has none of those constraints; it carries its own data and goes wherever it likes.

So when *should* a struct hold a reference? When the borrow is genuinely temporary and the performance of avoiding a copy matters: a parser that holds a view into a source string it's processing, a struct that exists only briefly within a function that owns the underlying data. These are real and useful, but they're intermediate patterns. For most types you design, owned fields are simpler, more flexible, and free of lifetime annotations entirely.

> **Best practice**
>
> Default to owned fields in structs. Reach for a reference field (and its lifetime parameter) only when you specifically want to *avoid copying* and the struct's lifetime is naturally bounded, like a short-lived view into data owned elsewhere. If adding `<'a>` to a struct makes its uses awkward (it can't be returned, stored, or moved where you need), that's the signal to switch the field to owned data instead.

## Quiz time

**Question #1**

Why does a struct with a `&str` field require a lifetime parameter?

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

Because the struct stores a borrow, and the borrowed data must stay alive at least as long as the struct, or the struct would hold a dangling reference (lesson [9.5](https://learnrust.net/chapter-9/dangling-references/)). The lifetime parameter (`<'a>`, with `text: &'a str`) declares that relationship so the compiler can enforce that no instance outlives the data it borrows. Without it, E0106.

</details>

**Question #2**

What does `struct Excerpt<'a> { text: &'a str }` guarantee about an `Excerpt` instance?

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

That the `Excerpt` cannot outlive the text it borrows: the instance is valid only for as long as the borrowed `&str` is valid. Create one borrowing from a `String`, and the borrow checker ensures the `Excerpt` is gone before (or when) that `String` is dropped.

</details>

**Question #3**

You're designing a `Config` struct that the program holds for its whole run. Should its `name` field be `String` or `&str`? Why?

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

`String` (owned). A long-lived, freely-stored struct should own its data so it carries no lifetime constraint and can live and move anywhere. A `&'a str` field would tie `Config`'s lifetime to some borrowed source and thread `<'a>` through everything, with no benefit here. Reference fields are for short-lived views where avoiding a copy matters, not for a config held for the whole program.

</details>

You've now written lifetimes on functions and structs. But you've also written hundreds of reference-taking functions *without* them, which means the compiler was filling them in. The next lesson reveals the three elision rules that let it do that, and why you'll rarely write `'a` in practice.

## Sitemap

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