1

I'm aware it is not much possible nor recommended to have both a field that owns a value, and another one that stores a reference to the same value in a struct.

I was experimenting a bit with a more elaborate case, and stumbled on what seems to be an equivalent problem. I tried my best to "beat" the borrow checker but even when I'm feeling like I have a solution, an error still rises that I do not fully understand.

The idea is I want to have a struct A that stores both an owned value of type C, and a value of type B, with B storing a reference to the field of type C in A:

struct C {}
struct B<'a> {
 c: &'a C,
}
struct A<'a> {
 b: B<'a>,
 c: C,
}

1st question:

I am still somewhat convinced that this should work as if we can prove (using explicit lifetimes) that A.c (so A) outlives B.c, we are good. As it is written: a struct B must not outlive its field B.c, which in turn makes it that A must not outlive it either. If B.c is set to a ref to A.c, then shouldn't all lifetimes be equal?

2nd question:

The more elaborate case I was talking about is that A.b actually stores a Box<dyn MyTrait>, and B now implements this trait. I don't know if that actually plays a big role there, but this definitely adds some troubles with 'static lifetimes.

Below, I tried to be as explicit as possible with the lifetimes, and I still get the error '`instance.c` does not live long enough'. I can't understand why, because lifetimes seem to resolve correctly in my head.

Note that the PhantomData is not needed to get the same error, as pointed by @kmdreko, and was an attempt to show the relation between 'a and 'b.

struct C<'b> {
 _p: PhantomData<&'b ()>,
}
struct B<'a, 'b> {
 c: &'a C<'b>,
}
trait BTrait {}
impl<'a, 'b> BTrait for B<'a, 'b> {}
struct Dummy {}
impl BTrait for Dummy {}
struct A<'a, 'b> where 'b: 'a {
 b: Box<dyn BTrait + 'a>,
 c: C<'b>,
}
fn main() {
 let c = C { _p: PhantomData };
 let dummy = Box::new(Dummy {});
 let mut instance = A {
 b: dummy,
 c,
 };
 let b = Box::new(B { c: &instance.c });
 instance.b = b;
}

Again, as I understand, &instance.c should live as long as instance lives. (削除) Plus C will at least live 'b, which outlives 'a so B and its stored reference should always be valid. (削除ここまで). Actually C lives at most 'b, so it enforces nothing with respect to 'a.

asked Oct 14, 2025 at 23:30
8
  • 1
    There is no way to "beat" the borrow checker and store a self-referential type. Your example will fail to compile the moment you try to move the struct (try to explain to yourself why). Commented Oct 14, 2025 at 23:40
  • "Plus C will at least live 'b" - By construction, a C<'b> cannot live longer than 'b. But the point seems moot to me anyway since 'b is not necessary to get the same error: playground Commented Oct 15, 2025 at 0:44
  • Rel: Why can't I store a value and a reference to that value in the same struct? Commented Oct 15, 2025 at 7:31
  • @Chayim Friedman By "beating" the borrow checker I meant proving explicitly that lifetime relations are OK. Commented Oct 15, 2025 at 8:41
  • @kmdreko You're right. PhantomData was added in an attempt to show the relation between 'a and 'b. And I misinterpreted what 'b means to C, corrected. Commented Oct 15, 2025 at 8:46

1 Answer 1

2

In your code, you have actually successfully created a self-referential data structure. I say that because if b were a bit different, you could get it to point to c in a similar way. This code compiles:

struct C {}
struct A<'a> {
 b: Option<&'a C>,
 c: C,
}
fn main() {
 let mut instance = A {
 b: None,
 c: C {},
 };
 instance.b = Some(&instance.c);
}

The reason the compiler rejects your code is because dyn Trait is opaque to the compiler. In particular, it cannot assume what it does when dropped. The field b will not be dropped until after its parent A is dropped, but you've tongue-tied the compiler that b may hold a reference to A (to access c). So b will have access to a dangling reference when dropped, which is obviously not allowed.

answered Oct 15, 2025 at 0:53
Sign up to request clarification or add additional context in comments.

3 Comments

Will A be dropped before its field A.b due to drop glue? Meaning that the drop will be called recursively to the Box after A is freed?
So after some digging, it seems that the struct A has to be mutably borrowable in order for the destructor to run. As instance.c is always immutably borrowed by its own other field, there is no solution, apart from using ManuallyDrop or other unsafe tricks. I'm still stuck on why this works with static dispatch though. Thanks to the fields drop order there is no error, but does this mean that no destructor is called, or that it doesn't need to mutably borrow the whole struct?
In the working example, the compiler knows that references (and options) don't have custom drop logic and thus does not encounter the same problem.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.