Returning both instance and reference

Hi, I'm completely new to Rust (my background is C++), so maybe I'm missing some feuture that may help. In my toy project, I got into issue which essence is (hopefully) extracted in the snippet below.

I want to return an instance + instance referencing it from a function. Of course, the instance must be heap-allocated so that its address will not change after return. So I basically want to return a pair (Box<X>, XRef) where lifetime of XRef is limited by lifetime of X stored inside Box<X> – the problem is shown as g() function.

It's rather academic question, I can get around with moving the problem down with f().

struct X {
    value: i32,
}

struct XRef<'a> {
    x: &'a X,
}

fn f<'a>(x: &'a X) -> XRef<'a> {
    XRef { x }
}

// I want to return boxed X together with XRef pointing to it
// the lifetime of returned Box and XRef should be the same
// but I don't know how can I denote it
fn g<'a>() -> (Box<X>, XRef<'a>) {
    let x = Box::new(X { value: 10 });
    let xref = XRef { x: &x };
    // this should be OK (altough a bit tricky), 
    // but I think compiler will not get it:
    // the X instance is boxed inside x, so the address of it
    // (pointed to by xref) will not change
    // but compiler will complain that it is a borrow from local variable
    (x, xref)
}

fn main() {
    {
        // this is OK
        let x = Box::new(X { value: 10 });
        let xref = f(&x);
        println!("{}", xref.x.value);
    }
    {
        // how should I do this?
        let (x, xref) = g();
        println!("{}", xref.x.value);
    }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `x`
  --> src/main.rs:36:14
   |
36 |         let (x, xref) = g();
   |              ^ help: consider prefixing with an underscore: `_x`
   |
   = note: `#[warn(unused_variables)]` on by default

error[E0515]: cannot return value referencing local variable `x`
  --> src/main.rs:24:5
   |
18 |     let xref = XRef { x: &x };
   |                          -- `x` is borrowed here
...
24 |     (x, xref)
   |     ^^^^^^^^^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `x` because it is borrowed
  --> src/main.rs:24:6
   |
16 | fn g<'a>() -> (Box<X>, XRef<'a>) {
   |      -- lifetime `'a` defined here
17 |     let x = Box::new(X { value: 10 });
18 |     let xref = XRef { x: &x };
   |                          -- borrow of `x` occurs here
...
24 |     (x, xref)
   |     -^-------
   |     ||
   |     |move out of `x` occurs here
   |     returning this value requires that `x` is borrowed for `'a`

error: aborting due to 2 previous errors

Some errors have detailed explanations: E0505, E0515.
For more information about an error, try `rustc --explain E0505`.
error: could not compile `playground`.

To learn more, run the command again with --verbose.

This is not possible in safe Rust. The ownership system prevents you from moving borrowed values, and it doesn't care about the heap.

Such tuple is an example of self-referential struct (search for many explanations in this forum), and it's not safe in Rust.

There are some workarounds for this, but generally you should consider changing the design not to need such thing (e.g. pass the owned thing as a borrowed argument to the function that will find the ref you want).

1 Like