Why can't I store a value and a reference to that value in the same struct?
I have a value and I want to store that value and a reference to something inside that value in my own type:
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)
}
Sometimes, I have a value and I want to store that value and a reference to that value in the same structure:
struct Combined<'a>(Thing, &'a Thing);
fn make_combined<'a>() -> Combined<'a> {
let thing = Thing::new();
Combined(thing, &thing)
}
Sometimes, I'm not even taking a reference of the value and I get the same error:
struct Combined<'a>(Parent, Child<'a>);
fn make_combined<'a>() -> Combined<'a> {
let parent = Parent::new();
let child = parent.child();
Combined(parent, child)
}
In each of these cases, I get an error that one of the values "does not live long enough". What does this error mean?
Answer
The error you are encountering is related to Rust's lifetime rules. Essentially, when you try to store a reference (&T
) inside a struct, Rust needs to ensure that the reference is valid for the duration of the struct's lifetime. If the reference you're storing is tied to a local variable, Rust can't guarantee its validity outside of the function scope.
Let's break down your examples and understand why this happens:
1. Storing a reference to a field inside a struct:
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) // Error: `thing` does not live long enough
}
Here, the Thing
is created inside the function make_combined
. You try to store a reference to thing.count
inside Combined
. The problem is that thing
is local to the function and is dropped at the end of the function scope. The reference &thing.count
becomes invalid once the function returns because the lifetime of thing
ends at the end of the function, so the reference is no longer valid.
Solution: You need to ensure that the reference lives longer than the function scope, meaning it needs to either come from a value that lives longer (such as one passed into the function) or be owned (not a reference).
2. Storing a reference to the value itself:
struct Combined<'a>(Thing, &'a Thing);
fn make_combined<'a>() -> Combined<'a> {
let thing = Thing::new();
Combined(thing, &thing) // Error: `thing` does not live long enough
}
In this case, you are trying to store a reference to the whole Thing
inside the Combined
struct. Similar to the previous example, the problem is that thing
is local to the function. When you return the Combined
struct, the reference &thing
points to a value that will be dropped when the function scope ends, so the reference becomes invalid.
Solution: Again, you need to ensure that the reference is valid for a longer lifetime. One way to fix this is to return thing
from the function as part of the Combined
struct, rather than referencing it.
3. Storing a reference to a child object:
struct Combined<'a>(Parent, Child<'a>);
fn make_combined<'a>() -> Combined<'a> {
let parent = Parent::new();
let child = parent.child();
Combined(parent, child) // Error: `parent` does not live long enough
}
In this case, parent
is a local variable in the function, and you're trying to store a reference to a Child
inside the Combined
struct. The problem is the same: parent
will be dropped at the end of the function, but the reference to Child
needs to be valid for as long as the Combined
struct is used. Since the reference inside child
is tied to the lifetime of parent
, it becomes invalid when parent
is dropped.
Solution: You can either:
- Return the
parent
andchild
as owned types (e.g., by cloning or moving theparent
). - Use a reference that lives long enough, e.g., pass
parent
andchild
in from outside the function.
What does "does not live long enough" mean?
This error means that you are trying to store a reference to a value that will be dropped (or no longer valid) before the reference is used. Rust enforces strict lifetime rules to prevent dangling references, ensuring that you cannot have a reference that outlives the data it points to.
In the examples you've provided:
- The
thing
orparent
you're creating in the function has a scope limited to the function. - A reference (
&thing
,&parent
) to this local variable is trying to escape the function and be stored in a struct that may outlive the function's scope. - Rust prevents this because it can't guarantee the validity of the reference once the function ends and the local variable is dropped.
How to fix this?
-
Avoid storing references to local variables: The simplest solution is to return owned data instead of references.
struct Combined(Thing, Thing); fn make_combined() -> Combined { let thing = Thing::new(); Combined(thing, thing) // Move `thing` instead of referencing it }
-
Extend the lifetime of the reference: You need to make sure the reference you're storing in your struct is valid for as long as you need it. This may involve passing in the data with a longer lifetime, or using
Arc
orRc
for shared ownership if the data is intended to be shared across different parts of the program. -
Use the
'static
lifetime: If you know that the data you are referring to will live for the entire duration of your program, you can use the'static
lifetime.struct Combined<'a>(Thing, &'a Thing); fn make_combined() -> Combined<'static> { static PARENT: Thing = Thing::new(); // A static, long-living Thing Combined(PARENT, &PARENT) // Reference to a static value }
By understanding and applying the rules of lifetimes, you can ensure that your references are valid and avoid the "does not live long enough" error.