JACK M
JACK M

Reputation: 2841

boxed object doesn't have a size known at compile-time

trait MyTrait {
    fn foo(&self);
}

struct A {}

impl MyTrait for A {
    fn foo(&self) {
        println!("A");
    }
}

struct B {}

impl MyTrait for B{
    fn foo(&self) {
        println!("B");
    }
}

enum MyEnum {
    A,
    B,
}

fn create_object(my_enum: MyEnum) -> Box<dyn MyTrait> {
    let boxed_value: Box<dyn MyTrait> = match my_enum {
        MyEnum::A => Box::new(A{}),
        MyEnum::B => Box::new(B{}),
    };
    boxed_value
}

struct C<Allocator>
where Allocator: MyTrait + ?Sized {
    a: Box<Allocator>,
}

impl<Allocator> C<Allocator>
where Allocator: MyTrait {
    pub fn new(a: Box<Allocator>) -> Self {
        Self {a: a}
    }
}


fn main() {
    let boxed_value = create_object(MyEnum::A);
    C::new(boxed_value);
}

compile error:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the size for values of type `dyn MyTrait` cannot be known at compilation time
  --> src/main.rs:49:12
   |
49 |     C::new(boxed_value);
   |     ------ ^^^^^^^^^^^ doesn't have a size known at compile-time
   |     |
   |     required by a bound introduced by this call
   |
   = help: the trait `Sized` is not implemented for `dyn MyTrait`
note: required by a bound in `C::<Allocator>::new`
  --> src/main.rs:39:6
   |
39 | impl<Allocator> C<Allocator>
   |      ^^^^^^^^^ required by this bound in `C::<Allocator>::new`
40 | where Allocator: MyTrait {
41 |     pub fn new(a: Box<Allocator>) -> Self {
   |            --- required by a bound in this associated function

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` (bin "playground") due to previous error

I am not sure what this means. Trait object doesn't have a fixed size. The boxed object wraps the trait object, so it has a fixed size. How to modify this code? I have to dynamically generate boxed trait object on demand.

Upvotes: 0

Views: 190

Answers (1)

prog-fh
prog-fh

Reputation: 16925

The type of boxed_value in main() is Box<dyn MyTrait>, which is a fat pointer because of dyn. But C::new() expects a Box<Allocator> where Allocator is a concrete type (implementing MyTrait), i.e. a normal pointer (not fat) because no virtual-table is needed here.

Even if a Box<dyn MyTrait> actually points toward a A, it is not the same type as Box<A>. Moreover, you cannot go back from a dyn type to a concrete type (except by downcasting with Any, which implies a runtime check).

I think you could simply use dyn MyTrait all along the way in C.

struct C {
    a: Box<dyn MyTrait>,
}

impl C {
    pub fn new(a: Box<dyn MyTrait>) -> Self {
        Self { a }
    }
}

I probably misunderstood the intention behind C. I thought the (dynamic) polymorphism relied only on boxed_value but if another level of (static) polymorphism is also needed on C, then the original code with + ?Sized in the implementation is probably much better (as stated in the comments).

(I let this answer visible in case it corresponds to the intended usage; I will delete it if the author of the question confirms that I'm totally wrong.)

Upvotes: 0

Related Questions