I've got a struct that holds a box of a dyn InnerTrait type:
struct HoldsBox {
boxed: Box<dyn InnerTrait>,
}
I'm running into an issue of not being able to move the box contents, even though I'm just trying to shadow the value into itself
impl HoldsBox {
fn change_box(&mut self, option: SomeEnum) {
self.boxed = self.boxed.apply_option(option);
// ^^^^^^^^^^ move error here, because of &mut ref of self
}
}
The problem occurs because the interior trait method takes the boxed self by value:
impl InnerTrait for InnerStruct {
fn apply_option(self: Box<Self>, option: SomeEnum) -> Box<dyn InnerTrait> {
// consume self and use to create a new boxed version
}
}
So it doesn't work even though the value is taken and immediately replaced back into itself.
I'm guessing the answer might involve some sync primitives, including at least a Mutex, but my first attempts with it just moved the error into the Mutex write operation. Can anyone point me to what I'm missing?
No, this is not related to synchronization. You can't move out of a reference because then it wouldn't point to anything, and that is a violation of the memory model of the language. So what you want isn't possible as-is.
You should probably just redesign the InnerTrait so that apply_option takes &mut self. Alternatively (but this is not elegant), if it is possible to come up with a dummy value of type Box<dyn InnerTrait>, you could use mem::replace().
The inner struct that the trait works on uses a typestate / type-setting inner function, but the inner trait needs to remain object safe:
impl InnerStruct<T> {
// This is what gets called inside the method which takes Box<Self>
fn change_type<T2>(self, new_type: T2) -> InnerStruct<T2> {
// replace type T with newly supplied T2
}
}
However, I am setting this up around a builder pattern, and the first setup is an entirely ZST struct, so mem::replace() will probably do the trick. Much appreciated!
you can modify the HoldsBox to hold an Option<Box<dyn InnerStruct>>, and then .take().unwrap() to take out the value, then wrap the whole expression in Some(...)
The second option had just one minor adjustment required, in that I had to add an .as_mut() before unwrapping to call an &mut self method on the option contents, but otherwise both forms work the same and are probably approximately equivalent. I guess the Option version would be just a hair more efficient as there at no time needs to be two boxes (even though one holds a ZST), and Option<Box<_>> is null pointer optimized...
Box<ZST> doesn't allocate. Instead it always returns the smallest non-zero pointer with the correct alignment. As a result, in theory, mem::replace with a zero sized type should be marginally more efficient as it avoids the branch to check for null when calling unwrap but is otherwise equivalent. In practice though, I'd expect the difference between the two to be negligible/nonexistent.
This has been incredibly interesting. I really wish I could mark two responses as solutions... But, I guess it goes to mem::replace() by the tiniest of margins! Thanks everyone!!