Using a closure to Box a trait

I have the following simplistic code which demonstrates my problem:

trait MyTrait {
    fn stuff(&self);
}

struct Foo();

impl MyTrait for Foo {
    fn stuff(&self) {
        println!("stuff");
    }
}

fn wrap(f: Foo) -> Box<dyn MyTrait> {
    Box::new(f)
}

fn create() -> Result<Box<dyn MyTrait>, ()> {
    println!("create");
    create_foo().map(|f| Box::new(f))
}

fn create_foo() -> Result<Foo, ()> {
    println!("create_foo");
    Ok(Foo())
}

fn main() {
    let result = create().unwrap();
    result.stuff();
}

(Playground)

Gives me this error:

error[E0599]: no function or associated item named `new` found for struct `std::boxed::Box<(dyn MyTrait + 'static)>` in the current scope
  --> src/lib.rs:21:58
   |
21 |     create_foo().map(|f| Box::<(dyn MyTrait + 'static)>::new(f))
   |                                                          ^^^ function or associated item not found in `std::boxed::Box<(dyn MyTrait + 'static)>`
   |
   = note: the method `new` exists but the following trait bounds were not satisfied:
           `dyn MyTrait : std::marker::Sized`

If I remove the closure and use the function the error goes away.

This line:

create_foo().map(|f| Box::new(f))

becomes:

create_foo().map(wrap)

I figure this is because the function tells the compiler what I am trying to do while the closure does not. Is there some syntax I can use which avoids the need for a function? Or is this the best approach?

Thank you.

Seems like the compiler isn't able to automatically cast your Box<Foo> to Box<dyn MyTrait>
The following line does the trick:

create_foo().map(|f| Box::new(f) as Box<dyn MyTrait>)

I thought about casting, but whenever I cast in other languages it is interpreted as if I am doing something "wrong." Is that something I should unlearn in Rust?

You can alternatively give the closure an explicit return type:

create_foo().map(|f| -> Box<dyn MyTrait> { Box::new(f) })

But I don't think there's anything wrong with the cast. You don't even need to say Box<dyn MyTrait> again; if you just tell the compiler where the cast is happening, it will figure out what type to cast to:

create_foo().map(|f| Box::new(f) as _)
1 Like

Thanks. I think I was looking for something along the lines of specifying the explicit return type.

In general, I'd say "kind of". Casting pointers/references/structs is safe and expected, but casting integers is still seen as fairly bad in Rust.

References, and things like Box, are all safe because Rust will prevent you from doing any bad casts - you can't cast from &A to &B unless A and B are related. In fact, it's required to do some operations, though in many cases you can ask rustc to do it implicitly (as in this case). It's also not allowed to cast between references/integers unless you use raw pointers - but even then, as long as you ensure you don't dereference an invalid pointer, it's totally valid and OK. There's no UB in casting between pointed-to types, only in dereferencing bad pointers.

Integer casting is, however, still "bad", mostly because it silently truncates when casting from bigger to smaller integers. For that case, using explicit conversion APIs like .into() and .try_into() is preferred so you can get explicit error management.

1 Like