Question

Why Rust Cannot Store a Value and a Reference to It in the Same Struct

rustreferencelifetimeborrow-checker

Question

I want to create a Rust type that stores both an owned value and a reference to that value, or to data derived from it.

For example:

#[derive(Copy, Clone)]
struct Thing {
    count: u32,
}

struct Combined<'a>(Thing, &'a u32);

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };
    Combined(thing, &thing.count)
}

I also run into the same issue when trying to store a value and a reference to the whole value in the same struct:

#[derive(Copy, Clone)]
struct Thing {
    count: u32,
}

struct Combined<'a>(Thing, &'a Thing);

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };
    Combined(thing, &thing)
}

And sometimes I get a similar lifetime error even when the second field is not obviously a direct reference, but is a value that internally borrows from the first one:

struct Parent;
struct Child<'a>(&'a Parent);

impl Parent {
    fn new() -> Self {
        Parent
    }

    fn child(&self) -> Child<'_> {
        Child(self)
    }
}

struct Combined<'a>(Parent, Child<'a>);

fn make_combined<'a>() -> Combined<'a> {
    let parent = Parent::new();
    let child = parent.child();
    Combined(parent, child)
}

In all of these cases, Rust reports that one of the values "does not live long enough." What exactly does that mean, and why does Rust reject this pattern?

Short Answer

By the end of this page, you will understand why Rust usually does not allow a struct to own a value and also store a reference into that same value. You will learn what lifetime errors like "does not live long enough" mean, why moving a value makes internal references unsafe, and which Rust-friendly alternatives are commonly used instead.

Concept

In Rust, a reference must always point to valid data for as long as the reference is used. This rule is enforced at compile time through borrowing and lifetimes.

The core problem in your examples is that the struct you are trying to build would be self-referential: one field owns the data, and another field points into that owned data.

That sounds reasonable at first, but Rust rejects it in ordinary safe code because owned values can be moved. When a value is moved, its memory location may change. If another field inside the same struct stores a reference to the old location, that reference could become invalid.

Consider this idea:

struct Combined<'a>(Thing, &'a u32);

If the second field references the first field's contents, then Combined contains a pointer into itself. But Rust does not guarantee that the struct will stay at one fixed memory address after it is created. Moving the struct could invalidate that internal reference.

This is why the compiler stops you earlier: it sees that the reference you are trying to store is tied to a local variable like thing, and that local variable will go out of scope when the function returns.

What "does not live long enough" means

A value's lifetime is the span of code during which it is valid.

In this function:

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };
    Combined(thing, &thing.count)
}
  • thing is created inside the function.
  • &thing.count borrows from thing.
  • At the end of the function, thing is dropped.
  • Returning a reference derived from thing would mean returning a reference to data that no longer exists.

Even though you also move thing into Combined, the borrow was created from the local variable thing before the returned struct exists as a stable, borrowable whole.

The same logic applies when a child value internally contains a reference to its parent:

struct Child<'a>(&'a Parent);

Even if Child<'a> is not written as a raw reference field in Combined, it still borrows from Parent, so the same lifetime rules apply.

Why this matters in real Rust programming

Rust's safety model prevents:

  • dangling references
  • use-after-free bugs
  • invalid pointers after moves

This is one of the reasons Rust programs can be memory-safe without a garbage collector. The compiler forces you to choose designs where ownership and borrowing stay valid.

Mental Model

Think of a struct as a box you can pick up and move.

  • The owned value is like a book placed inside the box.
  • A reference is like a sticky note that says, "look at page 10 of the book in this exact position."

If you move the whole box to another place and the sticky note depends on the book staying at the same exact spot, the note may no longer point correctly.

Rust avoids this by saying: if something inside the box points to another thing inside the same box, that is dangerous unless the box is guaranteed not to move.

Another way to think about it:

  • Owned data = you own the house.
  • Reference = you wrote down the house address.
  • If the house itself can be relocated, the old address becomes wrong.

Rust wants references to behave like reliable addresses, so it prevents patterns where moving values could silently break them.

Take Quiz

Syntax and Examples

The pattern that fails

Here is the common pattern that beginners try first:

#[derive(Copy, Clone)]
struct Thing {
    count: u32,
}

struct Combined<'a> {
    thing: Thing,
    count_ref: &'a u32,
}

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };

    Combined {
        thing,
        count_ref: &thing.count,
    }
}

This fails because count_ref borrows from thing, and thing is a local variable whose lifetime ends when the function returns.

A Rust-friendly alternative: store owned data only

Instead of storing both the value and a reference into it, store just the owned value and access the inner field when needed:

#[derive(Copy, Clone)]
struct Thing {
    count: u32,
}

struct Combined {
    thing: Thing,
}

impl Combined {
    fn count(&self) -> &u32 {
        &self.thing.count
    }
}

fn make_combined() -> Combined {
    Combined {
        thing: Thing { count: 42 },
    }
}

This works because no stored reference needs to outlive the constructor. The reference is created temporarily when count() is called.

Another alternative: store an index or key instead of a reference

If the borrowed value is part of a collection, it is often better to store a position:

struct Combined {
    values: Vec<u32>,
    selected: usize,
}

impl Combined {
    fn selected_value(&self) -> Option<&u32> {
        self.values.get(self.selected)
    }
}

This avoids self-references entirely.

Another alternative: clone small data

If the borrowed field is cheap to copy, just store it directly:

#[derive(Copy, Clone)]
struct Thing {
    count: u32,
}

struct Combined {
    thing: Thing,
    count: u32,
}

fn make_combined() -> Combined {
    let thing = Thing { count: 42 };
    Combined {
        thing,
        count: thing.count,
    }
}

For small Copy values like u32, this is simple and efficient.

When fixed-location patterns are needed

There are advanced solutions for true self-referential structures, such as:

  • Pin
  • arena allocation
  • crates designed for self-referential types
  • reference-counted shared ownership with indirection

But these are specialized tools, not the usual beginner solution.

Step by Step Execution

Consider this example:

#[derive(Copy, Clone)]
struct Thing {
    count: u32,
}

struct Combined<'a>(Thing, &'a u32);

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };
    let count_ref = &thing.count;
    Combined(thing, count_ref)
}

Step-by-step

1. thing is created

let thing = Thing { count: 42 };
  • thing is a local variable.
  • It lives only until the end of make_combined.

2. A reference is taken

let count_ref = &thing.count;
  • count_ref borrows part of thing.
  • That borrow is only valid while thing is still alive.

3. You try to return both

Combined(thing, count_ref)

This seems like it should work because thing is being moved into the returned value.

But the borrow checker reasons like this:

  • count_ref was created as a reference to the local variable thing.
  • The local variable thing is dropped at the end of the function scope.
  • Therefore, the returned reference would outlive its source.

So Rust rejects it.

Important subtlety: moving changes identity

Even if thing becomes part of the returned struct, the compiler does not treat &thing.count as automatically transforming into "a reference to the thing field inside Combined."

That would require constructing a reference into a value that is itself being moved during construction, which is exactly the unsafe case Rust avoids in normal structs.

Real World Use Cases

This concept matters whenever data relationships look like "one object owns data, another part points inside it."

Common situations

  • Parsing text: a parser owns a String, and tokens want to borrow slices from it.
  • Tree structures: a node owns children, and each child wants a direct reference back to the parent.
  • Configuration objects: one field stores full config data, another field wants to cache a borrowed part.
  • Database results: a row owns raw bytes, and decoded fields want to borrow from those bytes.
  • API responses: one struct owns response data, and another field tries to keep references into it.

Practical Rust approach

In real apps, developers usually avoid self-references by using one of these patterns:

  • compute borrowed views on demand
  • store IDs, keys, or indexes instead of references
  • split ownership across separate values
  • use Rc, Arc, or Box for indirection when appropriate
  • allocate long-lived backing storage in an arena

These designs are easier to reason about and work well with Rust's ownership model.

Take Quiz

Real Codebase Usage

In real Rust codebases, developers rarely store internal references in ordinary structs. Instead, they redesign the data model.

Common patterns

Guarded access through methods

Store only the owner, then borrow from it when needed:

struct Document {
    text: String,
}

impl Document {
    fn first_char(&self) -> Option<&str> {
        self.text.get(0..1)
    }
}

Store offsets instead of references

This is common in parsers and text processing:

struct Token {
    start: usize,
    end: usize,
}

struct ParsedFile {
    source: String,
    tokens: Vec<Token>,
}

impl ParsedFile {
    fn token_text(&self, token: &Token) -> &str {
        &self.source[token.start..token.end]
    }
}

Early validation, later borrowing

A constructor may validate indexes, lengths, or IDs first, then methods produce references later.

Shared ownership with indirection

Sometimes code stores heap-allocated data separately and references that stable allocation through smart pointers:

use std::rc::Rc;

struct Thing {
    count: u32,
}

struct Combined {
    thing: Rc<Thing>,
}

impl Combined {
    fn thing_ref(&self) -> &Thing {
        &self.thing
    }
}

This does not create a self-reference inside the same struct. Instead, the struct owns a pointer-like handle to heap data.

Error handling and validation

In production code, methods that derive views often return Option or Result rather than storing borrowed values permanently.

Common Mistakes

1. Assuming moving into a struct keeps references valid

Broken code:

struct Combined<'a> {
    thing: Thing,
    count_ref: &'a u32,
}

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };
    Combined {
        thing,
        count_ref: &thing.count,
    }
}

Why it fails:

  • count_ref borrows from the local variable thing
  • that local variable does not live long enough for the returned value

How to fix it:

  • store only thing
  • create the reference later with a method

2. Thinking lifetimes extend object ownership

A lifetime parameter like 'a does not make data live longer.

Broken idea:

fn make_combined<'a>() -> Combined<'a> {
    let thing = Thing { count: 42 };
    // adding 'a here does not help
    Combined(thing, &thing.count)
}

Why it fails:

  • lifetimes describe relationships between borrows
  • they do not turn local variables into long-lived values

3. Forgetting that hidden borrows still count

This also borrows:

struct Child<'a>(&'a Parent);

So this is still a self-referential pattern when both are stored together.

4. Trying to solve it with Copy or Clone

Making a type Copy does not make references safe.

Copy means values can be duplicated by simple bitwise copy. It does not change borrow validity.

5. Overusing references in owned structs

Beginners often think references are always more efficient.

In Rust, it is often better to:

  • own the data
  • clone small cheap values
  • store indexes instead of references

That usually leads to simpler and safer code.

6. Returning references to local variables

Broken code:

fn bad<'a>() -> &'a u32 {
    let x = 5;
    &x
}

This is the same core issue: the referenced value disappears when the function ends.

Comparisons

Self-reference vs Rust-friendly alternatives

ApproachStores internal reference?Safe in ordinary structs?Common use case
Own value + reference into same valueYesNoUsually rejected by borrow checker
Own value + method returns borrowNoYesMost common replacement
Own value + copied fieldNoYesSmall Copy data like numbers
Own collection + index/keyNoYesParsers, lookup tables, selected items
Rc/Arc indirectionNot internal self-referenceYesShared ownership across parts of a program
Pin + advanced unsafe patternsSometimesAdvanced onlySpecialized libraries and low-level code

Borrowed field vs computed-on-demand field

PatternExampleProsCons
Stored borrowcount_ref: &'a u32Fast access if it were validHard or impossible in normal owned structs
Computed borrowfn count(&self) -> &u32Simple and idiomaticRecomputes access each call, though usually trivial

Reference vs index

ChoiceBest whenBenefit
ReferenceData clearly outlives the borrowerDirect access
Index/offsetData is owned by the same structAvoids lifetime issues

For many designs, using an index or computing the reference when needed is the cleanest solution.

Take Quiz

Cheat Sheet

Core rule

A normal Rust struct generally cannot own a value and also store a reference into that same value.

Why

  • owned values can be moved
  • moving can change memory location
  • references must always stay valid
  • Rust prevents dangling internal references

Typical error meaning

"Does not live long enough" usually means:

  • you created a reference to a local variable
  • that local variable will be dropped too soon
  • the reference would outlive the data it points to

Common failing pattern

struct Combined<'a> {
    owner: Thing,
    borrowed: &'a u32,
}

Common fixes

Store only the owner

struct Combined {
    owner: Thing,
}

Borrow through a method

impl Combined {
    fn borrowed(&self) -> &u32 {
        &self.owner.count
    }
}

Store a copied value

struct Combined {
    owner: Thing,
    count: u32,
}

Store an index/offset

struct Combined {
    values: Vec<u32>,
    selected: usize,
}

Important reminder

Lifetimes:

  • describe borrow relationships
  • do not extend how long data lives

Advanced tools

Use only when necessary:

  • Pin
  • arena allocation
  • self-referential helper crates
  • Rc / Arc / Box indirection

FAQ

Why can I return owned data but not a reference to local data?

Owned data is moved out of the function, so ownership transfers safely. A reference only points to data; it does not own it. If the referenced local data is dropped, the reference becomes invalid.

Does adding a lifetime parameter make the local variable live longer?

No. Lifetimes describe how long borrows are valid relative to other values. They do not extend the lifetime of local variables.

Why does this fail even when the second field is not written as &T?

Because the type may still contain a borrow internally, such as Child<'a>. Rust checks the actual borrowing relationships, not just surface syntax.

Can Box<T> solve this problem?

Not by itself. Box<T> puts data on the heap, but if you still try to store a reference into your own owned data in the same struct, you run into the same design issue.

Is there any way to build self-referential structs in Rust?

Yes, but it usually requires advanced techniques such as Pin, special crate support, or carefully controlled unsafe code. Most applications should prefer simpler designs.

What is the easiest alternative in beginner Rust?

Store the owned value and create references from &self methods when needed.

When should I use indexes instead of references?

Use indexes when the referenced data lives inside a collection owned by the same struct. This is very common in parsers, tokenizers, and data-processing code.

Take Quiz

Mini Project

Description

Build a small Rust type that owns a string and tracks a selected word without storing a direct reference into itself. This demonstrates the Rust-friendly approach to replacing self-referential structs: store stable metadata like indexes, then derive references when needed.

Goal

Create a Document type that owns text, stores the start and end positions of a selected word, and returns that word as &str through a method.

Requirements

  • Create a Document struct that owns a String.
  • Store the selected word using start and end indexes, not a reference.
  • Implement a constructor that finds the first word in the string.
  • Add a method that returns the selected word as Option<&str>.
  • Demonstrate the type in main by printing the selected word.
Take Quiz

Keep learning

Related questions

View more