I have some structs containing some data that look like this:
struct A {
// Some data
}
struct B<'s> {
a: &'s mut A
c: C<'s>
}
impl<'s> B<'s> {
fn new(a: &'s mut A, d: &'s D) -> B<'s> {
let c = C::new(d);
B { a, c }
}
fn extract_c(self) -> C<'s> {
self.c
}
}
// A smart pointer provided by a library
struct C<'s> {
d: &'s D,
// Other fields holding some data, not referencing A
}
struct D {
// Some other data not referencing A in any way (provided by a library)
}
I also have functions that that create and modify said structs like this:
fn modify(b: &mut B) {
// Modifies b.a and b.c
}
fn construct_c<'s>(a: &'s mut A, d: &'s D) -> C<'s> {
let mut b = B::new(a, d); // C is created in B::new
modify(&mut b);
b.extract_c()
}
I want to use construct_c
somewhere else such that I can make another reference to A after obtaining the result from the call, like this:
fn main() {
let mut a = A::new();
let d = D::new();
let c = construct_c(&mut a, &d); // First mutable borrow of `a`
println!("{a}"); // Second borrow of `a`
let result = do_sth_with_c(c); // Move of `c`
do_sth_with_a(a);
// Some other code...
}
However, when I try to do that, the compiler says that when I call do_sth_with_c
, I am using the first mutable borrow of a
, even though c
doesn't hold the reference to a
provided to construct_c
.
When I removed println!("{a}");
and do_sth_with_a
the code compiles, but I really need to print that information and do it before calling do_sth_with_c
. Is there a way to tell the compiler that C
knows nothing about the reference to a
and that it is safe to make new references after calling construct_c
?
EDIT 1:
When I substituted all the references &mut A
with Rc<RefCell<A>>
the code compiles. But is there another way without using Rc
and RefCell
?
EDIT 2:
Following this answer, introducing another lifetime to B
seems to resolve the issue.
How B
changed:
struct B<'a, 's> {
a: &'a mut A,
c: C<'s>,
}
// All functions that use `B` need to be updated as well
You wrote in a comment about C
, "some other data not referencing A
in any way". But by using the same lifetime you're telling the compiler that they are related. So, change your definitions from
struct B<'s> {
a: &'s mut A
c: C<'s>
}
fn construct_c<'s>(a: &'s mut A, d: &'s D) -> C<'s> {
let mut b = B::new(a, d);
modify(&mut b);
b.extract_c()
}
to
struct B<'s, 'c> {
a: &'s mut A
c: C<'c>
}
fn construct_c<'s, 'c>(a: &'s mut A, d: &'c D) -> C<'c> {
let mut b = B::new(a, d);
modify(&mut b);
b.extract_c()
}
Now each lifetime can vary independently, and in particular, the 'c
lifetime is allowed to be longer than 's
.
That said, I have to warn you that you may be trying to use references where owned data would be better. A common beginner mistake is to try to build data storage structures out of references, and this is usually not a good idea because it either turns out impossible, or highly constrains how the structure can be used.