Expected trait object dyn FnOnce, found closure

Apologies if this is a basic question, but I'm having difficulty understanding what the compiler wants from me given the following error:

error[E0308]: mismatched types
 --> src/lib.rs:6:33
  |
6 |     myTraitObject.my_trait_func(|| {});
  |                                 ^^^^^ expected trait object `dyn FnOnce`, found closure
  |
  = note: expected trait object `(dyn FnOnce() + 'static)`
                  found closure `[closure@src/lib.rs:6:33: 6:38]`

Obviously, the types of my closure and the method signature conflicts, but I'm left not understanding why.

Here's my code:

trait MyTrait {
    fn my_trait_func(&self, somefunc: dyn FnOnce());
}

fn someOtherFunc(myTraitObject: &dyn MyTrait) {
    myTraitObject.my_trait_func(|| {});
}

(sample code: Rust Playground)

Any ideas? Thanks.

Thanks to my colleague, @stevewillcock, for reminding me that the closure needs to be borrowed and thus declared as &dyn MyTrait. Closures cannot be copied.

dyn Trait types are dynamically sized types, and cannot be passed as parameters directly. They need to be behind a pointer like &dyn Trait or Box<dyn Trait>.

trait MyTrait {
    fn my_trait_func(&self, somefunc: &dyn FnOnce());
}

fn someOtherFunc(myTraitObject: &dyn MyTrait) {
    myTraitObject.my_trait_func(&|| {});
}

Playground.

(If you add a body to the problematic function ({ todo!() }), you get an error about the size issue. But it's not checked for just a declaration. I guess it's a case of "totally okay to declare something which cannot be instantiated".)

1 Like

No. Closures can be Copy but the dyn Trait are not. dyn means its concrete type(and its size) can only be determined at runtime, but function parameters and return types must have statically known size.

You can allow passing closures directly by making the function generic like this stdlib example.

https://doc.rust-lang.org/stable/src/core/option.rs.html#435-456

1 Like

Thanks for the clarifications and solutions!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.