0

I have a Parent which contains an Option<Child> and each Child contains a Target.

struct MyError {}

struct Target {}

struct Child {
    target: Target,
}

struct Parent {
    child: Option<Child>,
}

Creating a Parent or Child can fail:

impl Child {
    fn allocate_self() -> Result<Self, MyError> {
        Ok(Self { target: Target {} }) // could fail
    }
}

impl Parent {
    fn allocate_self() -> Result<Self, MyError> {
        Ok(Self { child: None }) // could fail
    }
}

My goal is to:

  1. Allocate the Parent, returning an error on failure.
  2. Allocate the Child, returning an error on failure as well as freeing the Parent.
  3. Return the &mut Target on success.

So far I have:

impl Child {
    fn retrieve_target(this_opt: &mut Option<Self>) -> Result<&mut Target, MyError> {
        if this_opt.is_none() {
            *this_opt = Some(Self::allocate_self()?);
        }
        Ok(&mut this_opt.as_mut().unwrap().target)
    }
}

impl Parent {
    fn retrieve_target(this_opt: &mut Option<Self>) -> Result<&mut Target, MyError> {
        if this_opt.is_none() {
            *this_opt = Some(Self::allocate_self()?)
        }
        let this = this_opt.as_mut().unwrap();
        let result = Child::retrieve_target(&mut this.child);
        // GOAL: if Child::retrieve_target succeeds,
        //       return the result, otherwise turn ourselves back into a None
        if result.is_err() {
            drop(this_opt.take());
        }
        result
    }
}

The problem is that Parent::retrieve_target fails with a compiler error:

error[E0499]: cannot borrow `*this_opt` as mutable more than once at a time
  --> src/lib.rs:44:18
   |
35 |     fn retrieve_target(this_opt: &mut Option<Self>) -> Result<&mut Target, MyError> {
   |                                  - let's call the lifetime of this reference `'1`
...
39 |         let this = this_opt.as_mut().unwrap();
   |                    -------- first mutable borrow occurs here
...
44 |             drop(this_opt.take());
   |                  ^^^^^^^^ second mutable borrow occurs here
45 |         }
46 |         result
   |         ------ returning this value requires that `*this_opt` is borrowed for `'1`

I've tried an assortment of methods to try to tell the compiler that the Err part of the Result doesn't borrow this_opt but without success.

One solution is to first perform the allocations and then retrieve the target. However, in my use case, I have multiple layers (4 layer page table) and so ideally I'd be able to return the Target once I get to it. Since each layer has to do roughly the same thing, I'd like it to be possible that a macro with some simple name substitutions would be able to create a multi-layered structure.

Is this possible to write (ideally safely and concisely) and if so how?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Winestone
  • 1,500
  • 9
  • 19
  • Can you show your intended usage as code? – Sebastian Redl Jan 06 '19 at 10:19
  • @SebastianRedl Something like https://pastebin.com/ykMx8cei quite a bit messier, hopefully this captures all the details you were hoping for. Hopefully I haven't made any mistakes. – Winestone Jan 06 '19 at 10:55
  • *4 layer page table* — This is already covered by [Writing an OS in Rust (First Edition): Page Tables](https://os.phil-opp.com/page-tables/). – Shepmaster Jan 06 '19 at 17:28
  • 1
    You cannot yet. The duplicate explains why and when such ability may be added to Rust. – Shepmaster Jan 06 '19 at 17:50

0 Answers0