10

Why is the following code an error? There is a blanket implementation for [T; N] into Vec<T>, so why does dyn Foo not match Fooable in this context? Is there a workaround which does not involve cloning the Fooable?

trait Foo {}

struct Fooable {}

impl Foo for Fooable {}

pub fn main() {
    let bar: Vec<Box<dyn Foo>> = [
        Box::new(Fooable {}),
    ].into();
}

Error:

error[E0277]: the trait bound `Vec<Box<dyn Foo>>: From<[Box<Fooable>; 1]>` is not satisfied
  --> src/main.rs:10:7
   |
10 |     ].into();
   |       ^^^^ the trait `From<[Box<Fooable>; 1]>` is not implemented for `Vec<Box<dyn Foo>>`
   |
   = help: the following other types implement trait `From<T>`:
             <Vec<T, A> as From<Box<[T], A>>>
             <Vec<T, A> as From<VecDeque<T, A>>>
             <Vec<T> as From<&[T]>>
             <Vec<T> as From<&mut [T]>>
             <Vec<T> as From<BinaryHeap<T>>>
             <Vec<T> as From<Cow<'a, [T]>>>
             <Vec<T> as From<[T; N]>>
             <Vec<u8> as From<&str>>
           and 2 others
   = note: required because of the requirements on the impl of `Into<Vec<Box<dyn Foo>>>` for `[Box<Fooable>; 1]`

Playground

E_net4
  • 27,810
  • 13
  • 101
  • 139
frankplow
  • 502
  • 1
  • 12
  • 1
    Also related: https://stackoverflow.com/questions/25818082/vector-of-objects-belonging-to-a-trait https://stackoverflow.com/questions/27957103/how-do-i-create-a-heterogeneous-collection-of-objects – E_net4 Aug 14 '22 at 15:20

2 Answers2

11

You're right. There's a blanket implementation for From<[T; n]> on Vec<T>. That means you can convert [T; n] into Vec<T> with From::from or Into::into.

More specifically, that means you can convert a [Box<Fooable>; 1] into a Vec<Box<Fooable>>, or a [Box<dyn Foo>; 1] into a Vec<Box<dyn Foo>>. However, you have a [Box<Fooable>; 1] and want a Vec<Box<dyn Foo>>, and since the T is different, the blanket impl doesn't apply.

Use an explicit cast to get the behavior you want. You just have to convince Rust that the type of the array is [Box<dyn Foo>; 1], not [Box<Fooable>; 1] as it originally infers.

let bar: Vec<Box<dyn Foo>> = [
    Box::new(Fooable {}) as Box<dyn Foo>,
].into();
Silvio Mayolo
  • 62,821
  • 6
  • 74
  • 116
9

You can fix the error by manually casting the box like this:

trait Foo {}

struct Fooable {}

impl Foo for Fooable {}

pub fn main() {
    let bar: Vec<Box<dyn Foo>> = [
        // Turn a Box<Fooable> into a Box<dyn Foo>
        Box::new(Fooable {}) as Box<dyn Foo>,
    ].into();
}

Remember that by default, Rust builds replaces trait method calls directly with calls to the implementation if it exists. A object's "type" only exists at compile time, at run time there are no types. dyn types are different. They store some extra data that allows finding the actual variant of the method to call at runtime.

Why is assigning to a array directly OK though?

When you define a array of integers like this:

// this works
let k:[i8;3] = [1,2,3]

There is no conversion happening here. Even though the numbers are i32 by default the numbers in the array will be interpreted as i8 values.

It's the same in this case:

let k:Box<dyn T> = Box::new(V{});

Converting from a V{} to a Box<dyn T> requires a conversion, but the compiler can avoid the conversion by interpreting the right hand side as a Box<dyn T> from the start.

This works for arrays too, but not if there is a level of indirection:

// works
let k:[Box<dyn T>;1] = [Box::new(V{})]
// does not work
let b = [Box::new(Fooable {})];
let e:[Box<dyn Foo>;1] = b;

This is also the reason this is valid:

let bar: Vec<Box<dyn Foo>> = [
    Box::new(Fooable {}) as Box<dyn Foo>,
    Box::new(Fooable {}),
    Box::new(Fooable {}),
].into()

Even though we only have the as Box<dyn Foo> on the first element this changes how the entire array is interpreted.

mousetail
  • 7,009
  • 4
  • 25
  • 45
  • 1
    > Thus the conversion is not not free and requires a explicit step. Is there a reason that this does happen implicitly when not dealing with the vector? For example `bar: Box = Box::new(Fooable {});` is valid code. – frankplow Aug 14 '22 at 15:35
  • 1
    @frankplow A very good question that I don't know the answer to – mousetail Aug 14 '22 at 15:38
  • 3
    @frankplow @mousetail It's presumably because of the indirection through the `From` trait which can't do the cast implicitly. That's why `let baz: Box = Box::new(Fooable {}).into();` also doesn't work, but `let bar: [Box; 1] = [Box::new(Fooable {})];` does. – isaactfa Aug 14 '22 at 15:41
  • 1
    @mousetail I think you misread: `let bar: [Box; 1] = [Box::new(Fooable {})];` **does** work! Although only in a `let` statement, not in an assignment expression. You do need an explicit cast there. It's just that `From for dyn Trait` isn't implemented, which makes sense. – isaactfa Aug 14 '22 at 15:55