How to move `self` when using `dyn Trait`?

Here's an example that gets to what I am trying to do:

trait Builder {
    fn finish(self);
}

fn main() {
    let builder: Box<dyn Builder> = Box::new(make_builder());
    builder.finish();
}

This does not compile as using self in the function signature requires that Self: Sized but dyn types are not Sized. Adding the constraint trait Builder: Sized makes Box<dyn Builder> refuse to compile as dyn Builder does not meet the condition that it must be sized.

Is there a way of doing something with these semantics? For now I will just replace self with &mut self and accept the minor clunkiness it will come with. For reference, I am trying to refactor an enum which had far too many variants into a trait. The old enum version allowed me to safely put self into function signatures while still retaining the abstraction that Box<dyn _> provides.

1 Like

You can make it fn finish(self: Box<Self>); and let impl block to move out the box if needed.

But yeah, using trait object usually is less convenient than enum. If you feel too verbose on those large match block on every implementation, you can use enum_dispatch to reduce the boilerplate.

4 Likes

Accepted RFC #1909 allows this, and it's possible on nightly with the unsized_locals feature.

There appear to be some issues with the implementation, some leading to unsoundness (search for issues labeled F-unsized_locals on Github). But there's a hope that it will be stabilized eventually, and then the code you wrote will work with no additional changes.

3 Likes
1 Like

I don't think VLAs are relevant here. The example doesn't grow the stack by a size known at runtime. (In fact, it doesn't grow the stack by the size of Self at all.) Also, VLAs are not yet implemented in the compiler and one of the obstacles to doing so is the lack of consensus on what the syntax should be. While it's risky to make predictions, I think it's unlikely that Rust will end up in the C99 trap where the difference between a VLA and a regular array is invisible at declaration, precisely because nobody wants to deal with this kind of ambiguity.

(Also worth noting that if that kernel code were written in Rust, one ought to have used const instead of #define for those constants, and the compiler would have complained at that point. Except that in Rust the equivalent max! macro actually would be a constant expression, so it would have worked anyway.)

1 Like