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 foocreates a reference to the function-localfoo, but then you movefoointo a new instance ofParent, 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 giveBarandParentshared ownership of theFoo. (This is analogous to the C++std::shared_ptrtemplate, 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 anRcunless there is only oneRcin existence at that point.You can get around this with
CellorRefCell, which provide interior mutability.RefCellis 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:
Since the struct you're trying to create is self-referential, the non-
Rcway of handling this would involve pinning andunsafe. Basically you would have to create theFooat a stable memory location before you take any references to it, andPin<_>is required to ensure that the memory location of a value never changes. You likely would still needRefCellif you want the value to be mutable through both theParentand theBar.