How do I implement a structure with a callback with a compound data type as argument?

Viewed 125

I succeeded with primitive data types as arguments, but I have a difficult time doing it for compound data types in arguments.

fn main() {
    let renderer = Render::new("name1".to_string());
}

pub struct UpdateEvent {
    pub dt: f64,
}

pub struct Render<OnUpdate>
where
    OnUpdate: Fn(&mut UpdateEvent),
{
    pub name: String,
    pub f_on_update: OnUpdate,
}

impl<OnUpdate> Render<OnUpdate>
where
    OnUpdate: Fn(&mut UpdateEvent),
{
    pub fn new(name: String) -> Render<OnUpdate> {
        let f_on_update = |e: &mut UpdateEvent| {};
        Render { name, f_on_update }
    }
}

It throws 2 errors:

error[E0284]: type annotations needed: cannot satisfy `for<'r> <_ as FnOnce<(&'r mut UpdateEvent,)>>::Output == ()`
  --> src/main.rs:2:20
   |
2  |     let renderer = Render::new("name1".to_string());
   |                    ^^^^^^^^^^^ cannot satisfy `for<'r> <_ as FnOnce<(&'r mut UpdateEvent,)>>::Output == ()`
...
21 |     pub fn new(name: String) -> Render<OnUpdate> {
   |     -------------------------------------------- required by `Render::<OnUpdate>::new`

error[E0308]: mismatched types
  --> src/main.rs:23:24
   |
17 | impl<OnUpdate> Render<OnUpdate>
   |      -------- this type parameter
...
22 |         let f_on_update = |e: &mut UpdateEvent| {};
   |                           ------------------------ the found closure
23 |         Render { name, f_on_update }
   |                        ^^^^^^^^^^^ expected type parameter `OnUpdate`, found closure
   |
   = note: expected type parameter `OnUpdate`
                     found closure `[closure@src/main.rs:22:27: 22:51]`
   = help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `OnUpdate`

Where do I look?

1 Answers

The best way to get your code working, in my opinion, is to pass the value used for f_on_update via the new function, rather than define it within the new function:

fn main() {
    let renderer = Render::new("name1".to_string(), |_| {});
    let mut ue = UpdateEvent { dt: 5. };
    (renderer.f_on_update)(&mut ue);
}

pub struct UpdateEvent {
    pub dt: f64,
}

pub struct Render<OnUpdate>
where
    OnUpdate: Fn(&mut UpdateEvent),
{
    pub name: String,
    pub f_on_update: OnUpdate,
}

impl<OnUpdate> Render<OnUpdate>
where
    OnUpdate: Fn(&mut UpdateEvent),
{
    pub fn new(name: String, f: OnUpdate) -> Render<OnUpdate> {
        Render {
            name,
            f_on_update: f,
        }
    }
}

I think you're running into two errors in the given code:

  1. In the main function, there is not enough information given for the compiler to infer what the value of the type parameter OnUpdate is. This causes the first error, which is asking you to add type annotation. I believe, this also leads to the second error.
  2. In Render::new, the compiler doesn't know what the value of the type parameter OnUpdate is, so it can't know that the closure you're defining with |e: &mut UpdateEvent| {} is the correct type.
Related