I'm learning Rust (I'm a C++ dev) and I'm still getting used to the borrow checker. I have the following example (which is also on godbolt: https://godbolt.org/z/z873x9cPn):
struct Foo {
value: i32,
}
struct Bar <'a> {
foo: &'a mut Foo,
}
struct Parent <'a> {
foo: Foo,
bar: Bar<'a>,
}
impl <'a> Bar <'a> {
fn new(foo: &'a mut Foo) -> Self {
Self {
foo
}
}
}
impl <'a> Parent <'a> {
fn new() -> Self {
let mut foo = Foo{ value: 2};
let bar = Bar::new(&mut foo);
Self {
foo,
bar,
}
}
}
fn main () {
let _parent = Parent::new();
}
But when trying to compile I get an error:
error[E0515]: cannot return value referencing local variable `foo`
--> <source>:27:9
|
25 | let bar = Bar::new(&mut foo);
| -------- `foo` is borrowed here
26 |
27 | / Self {
28 | | foo,
29 | | bar,
30 | | }
| |_________^ returns a value referencing data owned by the current function
I've been looking through other posts but they don't solve exactly this dependency. Also, I've been trying to figure what to do but found no solution.
What is the best solution?
If you implemented this the same way in C++ that you did in Rust, you'd have undefined behavior. Rust is actually saving you here.
&mut foo
creates a reference to the function-local foo
, but then you move foo
into a new instance of Parent
, and so the reference isn't valid anymore. Rust catches this at compile time. You cannot move or drop a value while there is a reference to it.
The simplest way around this from the perspective of complexity of implementation would be to use a reference-counted smart pointer (Rc
) which will give Bar
and Parent
shared ownership of the Foo
. (This is analogous to the C++ std::shared_ptr
template, and has almost the same semantics.)
However, this is complicated by the fact that you are giving away mutable references to the Foo
. Rust does not allow you to obtain a mutable reference to the value held by an Rc
unless there is only one Rc
in existence at that point.
You can get around this with Cell
or RefCell
, which provide interior mutability. RefCell
is more flexible but has more runtime overhead, since it implements Rust's borrowing rules at runtime, meaning it can also panic if you use it incorrectly.
This is what that would look like:
use std::rc::Rc;
use std::cell::RefCell;
struct Foo {
value: i32,
}
struct Bar {
foo: Rc<RefCell<Foo>>,
}
struct Parent {
foo: Rc<RefCell<Foo>>,
bar: Bar,
}
impl Bar {
fn new(foo: Rc<RefCell<Foo>>) -> Self {
Self {
foo
}
}
}
impl Parent {
fn new() -> Self {
let mut foo = Rc::new(RefCell::new(Foo { value: 2 }));
let bar = Bar::new(foo.clone());
Self {
foo,
bar,
}
}
}
Since the struct you're trying to create is self-referential, the non-Rc
way of handling this would involve pinning and unsafe
. Basically you would have to create the Foo
at a stable memory location before you take any references to it, and Pin<_>
is required to ensure that the memory location of a value never changes. You likely would still need RefCell
if you want the value to be mutable through both the Parent
and the Bar
.