1

I have these (minimal, hence convoluted) structs:

#[derive(Debug)]
struct A(u32);
impl A {
    fn new() -> A{
        A(42)
    }
}

#[derive(Debug)]
struct B<'a>(&'a mut A);

I want to return a B from a function. However, B needs a reference to a new, unique A. My idea was to make the A in the same place, then shove them both inside a thing to keep them together:

use core::pin::Pin;

struct Thing<'a> {
    pub b: B<'a>,
    a_box: Pin<Box<A>>
}

fn get_thing() -> Thing<'static> {
    let mut a = Pin::new(Box::new(A::new()));
    Thing {
        b: B(&mut a),
        a_box: a
    }
}

fn main() {
    let x = get_thing();
    println!("{:?}", x.b);
}

However, this doesn't compile:

   Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing local variable `a`
  --> src/main.rs:22:5
   |
22 | /     Thing {
23 | |         b: B(&mut a),
   | |              ------ `a` is borrowed here
24 | |         a_box: a
25 | |     }
   | |_____^ returns a value referencing data owned by the current function

error[E0505]: cannot move out of `a` because it is borrowed
  --> src/main.rs:24:16
   |
22 | /     Thing {
23 | |         b: B(&mut a),
   | |              ------ borrow of `a` occurs here
24 | |         a_box: a
   | |                ^ move out of `a` occurs here
25 | |     }
   | |_____- returning this value requires that `a` is borrowed for `'static`

error: aborting due to 2 previous errors

How do I tell the compiler that I am, in fact, preserving the relationship between these two structs? Is there a good way, or should I resort to unsafe code?

wizzwizz4
  • 6,140
  • 2
  • 26
  • 62
  • @Jmb Not really; I'm already using `Pin` and `Box`, so very few of the _reasons_ for the error should still be around. Plus, that more explains why it's not possible, not how to make it possible. – wizzwizz4 Feb 17 '20 at 14:17
  • Hang on… perhaps `owning_ref` is the way to go? In which case, it would be a duplicate. – wizzwizz4 Feb 17 '20 at 14:18
  • Being pinned is not the same as having the `'static` lifetime. You can't just produce new references with `'static` lifetime, whether they are pinned or not. Additionally, you are not getting the reference to the pinned value via the `Pin` API, so no pinning guarantees are made. You probably need `Pin.get_mut()`. – Peter Hall Feb 17 '20 at 14:56
  • Also, this would be fundamentally unsound, since the reference is mutable. If were allowed to do what you are trying, you could end up with two mutable references to the same data, which would be UB. – Peter Hall Feb 17 '20 at 15:03
  • @PeterHall Good point. I do eventually need a mutable reference to the `A` object (since it needs an explicit destructor), but not until after I've dropped the `B` object. – wizzwizz4 Feb 17 '20 at 15:23
  • I think I'm going to have to implement this myself, because `owning_ref::OwningHandle` requires `B` to `DerefMut` to `A` – which it doesn't in my case. (Does `Box` actually need to be `Pin`ned? I'm not sure why I'm doing that, since it doesn't mean the _contents_ of the box don't move.) – wizzwizz4 Feb 17 '20 at 15:30
  • Make sure you read the section of the duplicate called "What about Pin?". Pin doesn't magically allow this. – Shepmaster Feb 17 '20 at 19:27
  • If anybody wants to see the code I ended up with, [here it is (AGPL, but the license doesn't cover the ideas themselves)](https://codeberg.org/wizzwizz4/sparkle/src/commit/bbde54f614ab2c933206fd3dd6c1ffe285858fee/src/display.rs). It's not good – you can see two different ways of doing it there – but it gets the job done. I didn't end up using `Pin`, because `Box` implements `stable_deref_trait::StableDeref`… but I never used that trait because my code wasn't generic. If you want to make a generic version, you'll want that trait, though. And also less unsafe code. – wizzwizz4 Feb 18 '20 at 13:53

0 Answers0