Why can't I .map(Box::new)?

For example (playground):

struct Foo {
    f: Option<Box<dyn Fn()>>
}

impl Foo {
    pub fn new(f: Option<impl Fn() + 'static>) -> Self {
        Self {
            //Why can't I just do this?
            //f: f.map(Box::new)
            
            //Instead I have to explicitly unwrap/rewrap:
            f: match f {
                Some(f) => Some(Box::new(f)),
                None => None
            }
        }
    }
}

fn main() {
    Foo::new(Some(|| {}));
}

Because you are not just calling Box::new on the value. You are doing two things:

  1. Call Box::new on the F.
  2. Convert the Box<F> into a Box<dyn Fn()>.

Here, F denotes the actual type of the closure in the option.

I'm not explicitly doing step 2 though... where is that happening?

The compiler automatically inserts the conversion when it notices that the types otherwise wouldn't match.

Step 2 can be done implicitly when moving a value around, which happens in your match, whereas .map signatures are very strict.

2 Likes

oh :sweat_smile:

how would it be done explicitly? via as ?

1 Like

You need that part to happen at a coercion site.

// E.G.
fn coercion_site(f: impl Fn() + 'static) -> Box<dyn Fn()> {
    Box::new(f)
}

impl Foo {
    pub fn new(f: Option<impl Fn() + 'static>) -> Self {
        Self { f: f.map(coercion_site) }
    }
}
3 Likes

Yes, you can do it explicitly with as

impl Foo {
    pub fn new(f: Option<impl Fn() + 'static>) -> Self {
        Self {
            f: match f {
                Some(f) => Some(Box::new(f) as Box<dyn Fn()>),
                None => None
            }
        }
    }
}
1 Like

f: f.map(|f| Box::new(f) as _) works as well.

8 Likes