Is it possible to return a struct containing reference to local variable and the variable it self by moving?

Hi, I'm new to rust and can't solve the problem by myself. The lifetime should be ok as C::b and C::a::b has the same scope.
Here's the example:

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

trait TraitA<'a> {
    fn new(b: &'a mut B) -> Self;
}

impl<'a> TraitA<'a> for A<'a> {
    fn new(b: &'a mut B) -> A<'a> {
        A {
            b
        }
    }
}

struct B;

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

impl C<'_> {
    fn new() -> Self {
        let mut b = B;
        C {
            a: A::new(&mut b),
            b,
        }
    }
}

fn main() {
    let c = C::new();
}

It is not possible. Rust calls it self-referential type, and the borrow checker forbids them completely.[1]

Returning structs from functions moves them, and moves may change their address, which invalidates all references to that struct. Rust chose to promise moves are simple memcpy without any extra code running, so it won't fix dangling references, and has to forbid them instead. For references into heap-allocated types it's not immediately unsafe, but it's still disallowed, because the borrow checker doesn't track such relationship between fields. There are some crates that hack around it, but they're generally cumbersome and sometimes unsafe/unsound.


You need to split the struct into two structs: one owning the data and having no references, and the other having references to a struct created earlier. And then you have to create them in two steps: first make the owning one and return it, and then in the parent scope create the view struct that uses temporary references. You can never store or return both at once, so you need to delay creation of the borrowing struct to the scope where it's needed, without ability of being returned from that function.

Alternatively, don't use temporary references, and use either Rc/Arc references, or some substitute like usize offsets into the data.


  1. except in compiler’s internal implementation of async fn futures, but that’s not available for normal Rust code. ↩︎

4 Likes

Thanks!