-1

How do to put two closures with the same definition into a Vec?

Here is a minimal reproducible example:

fn main() {
    let mut vec = Vec::new();
    vec.push(Box::new(|| println!("test")));
    vec.push(Box::new(|| println!("test2")));
    
    vec.iter().for_each(|f| (f)());
}

Which failed on compiling with following error:

error[E0308]: mismatched types
 --> src/main.rs:4:23
  |
3 |     vec.push(Box::new(|| println!("test")));
  |                       -- the expected closure
4 |     vec.push(Box::new(|| println!("test2")));
  |              -------- ^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
  |              |
  |              arguments to this function are incorrect
  |
  = note: expected closure `[closure@src/main.rs:3:23: 3:25]`
             found closure `[closure@src/main.rs:4:23: 4:25]`
  = note: no two closures, even if identical, have the same type
  = help: consider boxing your closure and/or using it as a trait object
mkrieger1
  • 19,194
  • 5
  • 54
  • 65
Preston
  • 180
  • 9
  • 2
    Need to point the common type so rust can do the coersion, `let mut vec: Vec> = Vec::new();` . otherwise Rust will assume it like `Vec>` – Ömer Erden Jul 31 '23 at 09:33
  • 2
    Found this duplicate [Expected closure, found a different closure](https://stackoverflow.com/questions/39083375/expected-closure-found-a-different-closure) by putting your title into the search bar... – cafce25 Jul 31 '23 at 09:37
  • 1
    @cafce25 I found that question before asking this question, but haven't gotten a solution for my question. – Preston Jul 31 '23 at 10:07

1 Answers1

1

You must explicitly tell that vec holds boxed trait objects:

fn main() {
    let mut vec: Vec<Box<dyn Fn()>> = Vec::new();
    vec.push(Box::new(|| println!("test")));
    vec.push(Box::new(|| println!("test2")));
    
    vec.iter().for_each(|f| (f)());
}

On the other hand you can leave it as it and "cast" boxed object with as keyword:

fn main() {
    let mut vec = Vec::new();
    vec.push(Box::new(|| println!("test")) as Box<dyn Fn()>);
    vec.push(Box::new(|| println!("test2")));
    
    vec.iter().for_each(|f| (f)());
}

note that you have to do it only once, since now rust will infer that type of vec is indeed Vec<Box<dyn Fn()>>, and further pushing will know to do this cast automatically.

Aleksander Krauze
  • 3,115
  • 7
  • 18
  • I am fairly certain that you can hint to the compiler that it needs to coerce, and have it find the right type with `as _` instead of giving the full type. For those all important keystroke savings – PatientPenguin Jul 31 '23 at 11:06
  • @PatientPenguin unfortunately this won't work and will result with the following error `an "as" expression can only be used to convert between primitive types or to coerce to a specific trait object`. This makes sense, because rust cannot know _which_ trait objects you want to store (`dyn Fn()`, `dyn FnOnce()`, `dyn FnMut()`, or something else entirely). – Aleksander Krauze Jul 31 '23 at 12:03
  • 1
    Oh you are totally right, my bad! – PatientPenguin Jul 31 '23 at 12:08
  • 1
    @AleksanderKrauze also for this specific case since the closures don't close over anything you don't have to box them, you can just coerce them to static function pointers (`fn()`). – Masklinn Jul 31 '23 at 13:21