'HashMap <Foo,Fn(Foo)> size for values of type `(dyn Fn(Foo) + 'static)` cannot be known

I'm trying to create a sort-of observer pattern in rust and I am getting a compile error that is basically telling me (I think) it doesn't know the size of a function param at runtime. I didn't realize the size of a function pointer was unknown at runtime. More importantly, I don't have a clue how to tell it the size

christianb@christianb-mac debug % cargo build
   Compiling pyrsia-blockchain v0.1.0 (/Users/christianb/dev/jfrog/rusty-brown)
error[E0277]: the size for values of type `(dyn Fn(Foo) -> Result<(), (dyn std::error::Error + 'static)> + 'static)` cannot be known at compilation time
   --> src/main.rs:45:16
    |
45  |     observers: HashMap<&'a Foo, OnFooDone>,
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `(dyn Fn(Foo) -> Result<(), (dyn std::error::Error + 'static)> + 'static)`
note: required by a bound in `HashMap`

error[E0277]: the size for values of type `(dyn std::error::Error + 'static)` cannot be known at compilation time
   --> src/main.rs:45:16
    |
45  |     observers: HashMap<&'a Foo, OnFooDone>,
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `(dyn std::error::Error + 'static)`
note: required by a bound in `Result`

error[E0277]: the size for values of type `(dyn std::error::Error + 'static)` cannot be known at compilation time
   --> src/main.rs:49:54
    |
49  |     pub fn submit_foo(&mut self, foo: &Foo, on_done: OnFooDone) -> &Self {
    |                                                      ^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `(dyn std::error::Error + 'static)`
note: required by a bound in `Result`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `pyrsia-blockchain` due to 3 previous errors

use std::collections::HashMap;
use std::error::Error;

fn main() {
    let mut holder = Holder {
        observers: HashMap::new()
    };
    let foo0 = Foo {
        id: 0,
        stuff: **"hello",
    };
    let foo1 = Foo {
        id: 1,
        stuff: **"world",
    };
    let mut foo2 = Foo {
        id: 2,
        stuff: **"bob",
    };
    let mut magic_num = 5;
    let mut closure = |foo| {
        println!("Slow");
        magic_num += foo.id;
        foo.id
    };

    holder.submit_foo(&foo0, |f| {
        println!("received foo {}", f.id)?
    });
    holder.submit_foo(&foo1, |f| {
        println!("received foo2 {}", f.id)?
    });
    holder.submit_foo(&foo2, closure);

    holder.notify_all();
}

#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Foo {
    id: u64,
    stuff: str,
}
type OnFooDone = dyn Fn(Foo) -> Result<(),dyn Error>;
pub struct Holder<'a> {
    observers: HashMap<&'a Foo, OnFooDone>,
}

impl Holder<'_> {
    pub fn submit_foo(&mut self, foo: &Foo, on_done: OnFooDone) -> &Self {
        self.observers.insert(foo, on_done);
        self
    }
    pub fn notify_all(self) -> Self {
        self.observers.iter().for_each(|k, f| f(k));
        self
    }
}




Solution 1:[1]

The problem is with:

type OnFooDone = dyn Fn(Foo) -> Result<(),dyn Error>;

This is a so-called unsized type because its size is not known at compile time. It's not a function pointer.

Let's start with the type itself. Imagine if you have just a regular function, but also a closure. It's pretty obvious now, that the closure is not the same size as the function pointer, but both implement Fn.

The same issue is present with the Result. What if the function returns several different types that implement dyn Error ?

The solution is to either use references or box the dynamically sized types:

  • You can return a boxed error, such as Box<dyn Error>> instead of just dyn Error
  • You can either box the Fn, just like we did with error above, or store a reference: HashMap<&'a Foo, &'a OnFooDone>

Thus we get:

type OnFooDone = dyn Fn(Foo) -> Result<(),Box<dyn Error>>;

pub struct Holder<'a> {
    observers: HashMap<&'a Foo, &'a OnFooDone>,
    // or with a boxed triat
    observers: HashMap<&'a Foo, Box<OnFooDone>>,
}

Resources:

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1