How to move Box into multiple variables in a loop and borrow mutably?

I am trying to take values and move them through a pipeline, where in each stage a different action takes place.

In a simple example which does not use Box<T> I can make this work:

let mut source: Vec<u32> = vec![4, 3, 2];
let mut stage_a: Option<u32> = Some(1);
let mut stage_b: Option<u32> = None;
let mut stage_c: Option<u32> = None;

while stage_a.is_some() || stage_b.is_some() || stage_c.is_some() {
    // Stage c
    match stage_b {
        None => stage_c = None,
        Some(v) => stage_c = Some(v),
    }
    
    // Stage b
    match stage_a {
        None => stage_b = None,
        Some(v) => stage_b = Some(v),
    }
    
    // Stage a
    match source.pop() {
        None => stage_a = None,
        Some(v) => stage_a = Some(v),
    }
}

Rust Playground

However in my actual use case I need to use Box<dyn SomeTrait> since I am using trait objects instead of u32s. Additionally the trait requires &mut self, so I have to borrow the boxes mutably:

(Note for the simplicity of my self contained code example I have left out the trait, instead I just borrow mutably to achieve the same error)

let mut source: Vec<u32> = vec![4, 3, 2];
let mut stage_a: Option<Box<u32>> = Some(Box::new(1));
let mut stage_b: Option<Box<u32>> = None;
let mut stage_c: Option<Box<u32>> = None;

while stage_a.is_some() || stage_b.is_some() || stage_c.is_some() {
    // Stage c
    match &mut stage_b {
        None => stage_c = None,
        Some(v) => stage_c = Some(*v),
    }
    
    // Stage b
    match &mut stage_a {
        None => stage_b = None,
        Some(v) => stage_b = Some(*v),
    }
    
    // Stage a
    match source.pop() {
        None => stage_a = None,
        Some(v) => stage_a = Some(Box::new(v)),
    }
}

Playground Link

The above example will not compile with the error:

error[E0507]: cannot move out of `*v` which is behind a mutable reference

My question is how do I make this pattern work? I feel as if I am going about this the wrong way. I somehow need to move each boxed trait object through the pipeline. To me the simplest way would be to have an individual variable for each stage. However doing so causes me to fight with the borrow checker.

I have been trying to figure this out for the last 2 weeks without success. I could use some help figuring out the correct way to achieve this pattern.

You can probably use Option::take here

// Stage c
stage_c = stage_b.take();

// Stage b
stage_b = stage_a.take();

// Stage a
stage_a = source.pop().map(Box::new);

Also, it's probably easier to debug this by creating a new type that isn't Copy.

2 Likes

Thank you. This did indeed work.

Is the reason this works because Option::take takes ownership of the value? Making it so the value does not need to be copied or cloned?

Yes. The number of Boxs does not change, so no cloning is needed to remain sound.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.