Question
Why Rust Cannot Store a Value and a Reference to It in the Same Struct
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)
}
thingis created inside the function.&thing.countborrows fromthing.- At the end of the function,
thingis dropped. - Returning a reference derived from
thingwould 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.
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 };
thingis a local variable.- It lives only until the end of
make_combined.
2. A reference is taken
let count_ref = &thing.count;
count_refborrows part ofthing.- That borrow is only valid while
thingis 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_refwas created as a reference to the local variablething.- The local variable
thingis 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, orBoxfor 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.
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_refborrows from the local variablething- 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
| Approach | Stores internal reference? | Safe in ordinary structs? | Common use case |
|---|---|---|---|
| Own value + reference into same value | Yes | No | Usually rejected by borrow checker |
| Own value + method returns borrow | No | Yes | Most common replacement |
| Own value + copied field | No | Yes | Small Copy data like numbers |
| Own collection + index/key | No | Yes | Parsers, lookup tables, selected items |
Rc/Arc indirection | Not internal self-reference | Yes | Shared ownership across parts of a program |
Pin + advanced unsafe patterns | Sometimes | Advanced only | Specialized libraries and low-level code |
Borrowed field vs computed-on-demand field
| Pattern | Example | Pros | Cons |
|---|---|---|---|
| Stored borrow | count_ref: &'a u32 | Fast access if it were valid | Hard or impossible in normal owned structs |
| Computed borrow | fn count(&self) -> &u32 | Simple and idiomatic | Recomputes access each call, though usually trivial |
Reference vs index
| Choice | Best when | Benefit |
|---|---|---|
| Reference | Data clearly outlives the borrower | Direct access |
| Index/offset | Data is owned by the same struct | Avoids lifetime issues |
For many designs, using an index or computing the reference when needed is the cleanest solution.
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/Boxindirection
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.
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
Documentstruct that owns aString. - 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
mainby printing the selected word.
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.