0

I'm new to rust and I'm trying to rewrite library from kotlin to rust. It is callback event library. When trying to return closure from another closure it breaks on_event function that registries callbacks and also breaks the invoker function which gets the invoker function due to lifetimes in the generic for invoker. I was asking the problem on many forums and couldn't get help.

Code

lib.rs

mod event;

#[cfg(test)]
mod tests {
    use crate::event::*;
    fn test(mut x: i32,y: i32, entries: &[&dyn Fn(i32)]) -> i32 {
        for entry in entries {
            x *= 3;
            (entry)(x);
        }
        x * y
    }
    fn invoker<'a>(entries: &'a [&'a dyn Fn(i32)]) -> impl Fn(i32) -> i32 + 'a {
        move |y|test(1,y,&entries)
    }
    #[test]
    fn it_works() {
        let mut event_test = EventKey::new(invoker);
        let test = &|x: i32| {
            println!("Hello {}", x);
        };
        event_test.on_event(test);
        event_test.on_event(test);
        event_test.on_event(test);
        let result = event_test.invoke()(10);
        assert_eq!(result, 270);
    }
}

event.rs

/// Event file
/// I - invoker type
/// R - additional return type
/// C - Callback arguments
/// Please know that this applies to everything in this file

pub struct EventKey<I, C> {
    invoker: I,
    callbacks: Vec<C>
}

impl<I,C,R> EventKey<I,C>
where I: Fn (&[C]) -> R
{
    pub fn new(invoker: I) -> EventKey<I, C> {
        EventKey {
            invoker,
            callbacks: Vec::new()
        }
    }

    pub fn on_event(&mut self, callback: C) {
        self.callbacks.push(callback);
    }

    pub fn invoke(&self) -> R {
        (self.invoker)(&self.callbacks)
    }
}

The errors

  1. Without lifetimes
error[E0700]: hidden type for `impl Fn(i32) -> i32` captures lifetime that does not appear in bounds
  --> src/lib.rs:14:9
   |
13 |     fn invoker(entries: &[&dyn Fn(i32)]) -> impl Fn(i32) -> i32 {
   |                         --------------- hidden type `[closure@src/lib.rs:14:9: 14:17]` captures the anonymous lifetime defined here
14 |         move |y|test(1,y,&entries)
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
help: to declare that `impl Fn(i32) -> i32` captures `'_`, you can introduce a named lifetime parameter `'a`
   |
13 |     fn invoker<'a>(entries: &'a [&'a dyn Fn(i32)]) -> impl Fn(i32) -> i32 + 'a  {
   |               ++++           ++   ++                                      ++++
  1. With lifetimes
error[E0599]: the method `on_event` exists for struct `EventKey<for<'a> fn(&'a [&'a (dyn Fn(i32) + 'a)]) -> impl Fn(i32) -> i32 + 'a {invoker}, &dyn Fn(i32)>`, but its trait bounds were not satisfied
  --> src/lib.rs:22:20
   |
22 |         event_test.on_event(test);
   |                    ^^^^^^^^ method cannot be called due to unsatisfied trait bounds
   |
  ::: src/event.rs:7:1
   |
7  | pub struct EventKey<I, C> {
   | ------------------------- method `on_event` not found for this struct
   |
note: the following trait bounds were not satisfied:
      `<for<'a> fn(&'a [&'a (dyn Fn(i32) + 'a)]) -> impl Fn(i32) -> i32 + 'a {invoker} as FnOnce<(&[&dyn Fn(i32)],)>>::Output = _`
      `for<'a> fn(&'a [&'a (dyn Fn(i32) + 'a)]) -> impl Fn(i32) -> i32 + 'a {invoker}: Fn<(&[&dyn Fn(i32)],)>`
  --> src/event.rs:13:10
   |
12 | impl<I,C,R> EventKey<I,C>
   |             -------------
13 | where I: Fn (&[C]) -> R
   |          ^^^^^^^^^^^^^^
   |          |            |
   |          |            unsatisfied trait bound introduced here
   |          unsatisfied trait bound introduced here
   = note: the following trait bounds were not satisfied:
           `for<'a> fn(&'a [&'a (dyn Fn(i32) + 'a)]) -> impl Fn(i32) -> i32 + 'a {invoker}: FnMut<(&[&dyn Fn(i32)],)>`
           which is required by `for<'a> fn(&'a [&'a (dyn Fn(i32) + 'a)]) -> impl Fn(i32) -> i32 + 'a {invoker}: Fn<(&[&dyn Fn(i32)],)>`
           `for<'a> fn(&'a [&'a (dyn Fn(i32) + 'a)]) -> impl Fn(i32) -> i32 + 'a {invoker}: FnOnce<(&[&dyn Fn(i32)],)>`
           which is required by `for<'a> fn(&'a [&'a (dyn Fn(i32) + 'a)]) -> impl Fn(i32) -> i32 + 'a {invoker}: Fn<(&[&dyn Fn(i32)],)>`
1
  • Your test function captures the parameter entries without specifying the lifetime of that parameter. Add lifetimes to your test function just like you added to your trait. Commented Mar 15, 2023 at 21:40

1 Answer 1

0

If you make sure that test in it_works is of the correct type:

let test: &dyn Fn(i32) = &|x| {
    println!("Hello {}", x);
};

You have to annotate the lifetimes in the implementation to match that of your usage:

impl<I, C> EventKey<I, C> {
    pub fn new(invoker: I) -> EventKey<I, C> {
        EventKey {
            invoker,
            callbacks: Vec::new(),
        }
    }

    pub fn on_event(&mut self, callback: C) {
        self.callbacks.push(callback);
    }

    pub fn invoke<'a, R>(&'a self) -> R
    where
        I: Fn(&'a [C]) -> R + 'a,
        R: 'a,
        C: 'a,
    {
        (self.invoker)(&self.callbacks)
    }
}

Note: After correctly annotating the lifetimes the compiler then complains that R is not constrained and rightfully so, to fix it I moved the bounds and type parameter to apply to the invoke method only.

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.