7
trait FooTrait {}

struct FooStruct;

impl FooTrait for FooStruct {}

fn main() {
    let maybe_struct: Option<dyn FooStruct> = None;

    //  Does not compile
    let maybe_trait: Option<Box<dyn FooTrait>> = maybe_struct.map(Box::new);

    // Compiles fine
    let maybe_trait: Option<Box<dyn FooTrait>> = match maybe_struct {
        Some(s) => Some(Box::new(s)),
        None => None,
    };
}
error[E0404]: expected trait, found struct `FooStruct`
 --> src/main.rs:9:34
  |
9 |     let maybe_struct: Option<dyn FooStruct> = None;
  |                                  ^^^^^^^^^ not a trait

Rustc 1.23.0. Why doesn't the first approach compile? Am I missing something obvious, or... huh?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Tomáš Dvořák
  • 1,490
  • 1
  • 9
  • 20

1 Answers1

8

Box::new only works with sized types; that is, it takes a value of a sized type T and returns Box<T>. In certain places a Box<T> can be coerced into a Box<U> (if T: Unsize<U>).

Such coercion does not happen in .map(Box::new), but does in Some(Box::new(s)); the latter is basically the same as Some(Box::new(s) as Box<FooTrait>).

You could create (in nightly) your own box constructor that returns boxes of unsized types like this:

#![feature(unsize)]

fn box_new_unsized<T, U>(v: T) -> Box<U>
where
    T: ::std::marker::Unsize<U>,
    U: ?Sized,
{
    Box::<T>::new(v)
}

and use it like .map(box_new_unsized). See Playground.

Stefan
  • 5,304
  • 2
  • 25
  • 44
  • 3
    Aha, I was missing the `as Box` implicitly happenning behind the scenes, and the (in retrospect obvious) fact that the `as` is *NOT* a no-op, creating a trait object is not just a play with type ascriptions, but an actual operation. If I want to use `.map`, I can explicitily `let maybe_trait = maybe_struct.map(|x| Box::new(x) as Box);`. I'll yet have to fully digest coerctions, `Unsize` and friends. Thanks. – Tomáš Dvořák Feb 19 '18 at 13:23
  • @TomášDvořák Btw: you can often omit the target type in `as`-expressions like this: `|x| Box::new(x) as _`. – Stefan Feb 19 '18 at 14:13
  • This `as Box` syntax also help me as well with a fairly different context than the problem posed here. – MikeTheSapien Aug 17 '21 at 09:12