Hi all, I am very new to Rust and quite excited about it, but I am stuck on a particular issue. Consider these two structs:
struct A {
x: u32
}
struct B<'a> {
a: &'a A
}
impl<'a> B<'a> {
fn new(a: &'a A) -> Self {
Self { a }
}
}
I cannot change these structs since they are part of an external crate. Because A and B are implementation details, I want to wrap them in a single struct that implements certain traits and, importantly, ensures that the relevant instance of A lives long enough for B::a to remain a valid reference.
My naive attempt was this:
struct C<'a> {
a: A,
b: B<'a>
}
impl<'a> C<'a> {
fn new() -> Self {
let a = A { x: 0 };
let b: B<'a> = B::new(&a);
C { a, b }
}
}
But this leads to the error: error[E0597]: a does not live long enough
In C++, I'd do this with a heap allocation such that, even when moving the pointer, the reference remains valid. So I tried using Box<A> instead of A within struct C, but the same error prevails. (And then Pin<Box<A>>.)
In other words, I want to avoid the need for the user of C to allocate a struct A. I want a single instance of C to properly manage both A and B referencing said A.
I understand the error message, but I am at a loss for how to work around this considering that I cannot change A or B. I'd very much appreciate any pointers!
The keyword to search for is "self-referential" (or "self-referencing") structs. Rust doesn't support this natively, but typically one can either try to avoid it, or make it work with crates like self_cell - Rust or yoke - Rust
Thank you for the helpful responses @steffahn and @vague, I really appreciate it!
@steffahn, you wrote "typically one can either try to avoid it". If I cannot modify A and B, is there a straightforward way to work around this problem?
No, avoiding the problem would either mean re-structuring the module that defines A and B such that you don’t need the struct B with a lifetime (e.g. with an alternative “owned” API or something..), or it means that you do end up offering your wrapping API in a similarly split manner, either living with the “ need for the user of C to allocate a struct A” that you wanted to avoid, or if that’s too much “implementation detail” coming up with your own 2-part wrapping API, where one half (say C1) contains the A and another half (say C2<'a>) contains the B<'a>.
Another way to look at this is that it normally isn't a good idea for a library crate to have an API where a struct contains a reference, since this makes it very difficult to use.
There are cases where this is Ok, for example when B is intended to be a temporary or very short lived value. In that case, you normally wouldn't normally need to put A and B together in another struct.
If you can be more concrete -- tell us the crate and why you want to put A and B together -- then we may be able to say more about the specific situation.