Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save varqox/484ef495c54f42cdebf5c1a02fe8d4c6 to your computer and use it in GitHub Desktop.
Save varqox/484ef495c54f42cdebf5c1a02fe8d4c6 to your computer and use it in GitHub Desktop.
Solution to Rust errors of kinds: "implementation of `Fn` is not general enough" (works with FnMut and FnOnce as well) or "lifetime may not live long enough"

Solution to Rust errors of kinds:

  • implementation of Fn is not general enough (works with FnMut and FnOnce as well)
  • lifetime may not live long enough
use std::future::Future;

trait Callback<Arg, Res> {
    type Output: Future<Output = Res>;

    fn call(&self, arg: Arg) -> Self::Output;
}

impl<Arg, Res, F, Fut> Callback<Arg, Res> for F
where
    F: Fn(Arg) -> Fut,
    Fut: Future<Output = Res>,
{
    type Output = Fut;
    fn call(&self, arg: Arg) -> Fut {
        self(arg)
    }
}

async fn run_callback<F>(callback: F)
where
    for<'a> F: Callback<&'a mut i32, i32>,
{
    for mut i in 0..10 {
        let res = callback.call(&mut i).await;
        println!("{}", res);
    }
}

async fn test() {
    async fn callback(x: &mut i32) -> i32 {
        *x += 10;
        println!("{}", x);
        *x + 100
    }
    run_callback(callback).await;
    // Does not work:
    // run_callback(|x: &mut i32| async move {
    //     *x += 10;
    //     println!("{}", x);
    //     *x + 100
    // }).await;
}

trait CallbackWithState<State, Arg, Res> {
    type Output: Future<Output = Res>;

    fn call(&self, state: State, arg: Arg) -> Self::Output;
}

impl<State, Arg, Res, F, Fut> CallbackWithState<State, Arg, Res> for F
where
    F: Fn(State, Arg) -> Fut,
    Fut: Future<Output = Res>,
{
    type Output = Fut;
    fn call(&self, state: State, arg: Arg) -> Fut {
        self(state, arg)
    }
}

async fn run_callback_with_state<State, F>(state: &mut State, callback: F)
where
    for<'state, 'arg> F: CallbackWithState<&'state mut State, &'arg mut i32, i32>,
{
    for mut i in 0..10 {
        let res = callback.call(state, &mut i).await;
        println!("{}", res);
    }
}

async fn test_with_state() {
    async fn callback(state: &mut String, x: &mut i32) -> i32 {
        *x += 10;
        state.push_str(&x.to_string());
        println!("{}", x);
        *x + 100
    }
    run_callback_with_state(&mut String::new(), callback).await;
    // Does not work:
    // run_callback_with_state(&mut String::new(), |state: &mut String, x: &mut i32| async move {
    //     *x += 10;
    //     state.push_str(&x.to_string());
    //     println!("{}", x);
    //     let res = *x + 100;
    // });
}

If you want to restrict the Callback trait to take references only you have to use explicit lifetimes like so:

use std::future::Future;

trait Callback<'arg, Arg: 'arg, Res> {
    type Output: Future<Output = Res>;

    fn call(&self, arg: &'arg mut Arg) -> Self::Output;
}

impl<'arg, Arg: 'arg, Res, F, Fut> Callback<'arg, Arg, Res> for F
where
    F: Fn(&'arg mut Arg) -> Fut,
    Fut: Future<Output = Res>,
{
    type Output = Fut;
    fn call(&self, arg: &'arg mut Arg) -> Fut {
        self(arg)
    }
}

async fn run_callback<F>(callback: F)
where
    for<'a> F: Callback<'a, i32, i32>,
{
    for mut i in 0..10 {
        let res = callback.call(&mut i).await;
        println!("{}", res);
    }
}

async fn test() {
    async fn callback(x: &mut i32) -> i32 {
        *x += 10;
        println!("{}", x);
        *x + 100
    }
    run_callback(callback).await;
    // Does not work:
    // run_callback(|x: &mut i32| async move {
    //     *x += 10;
    //     println!("{}", x);
    //     *x + 100
    // }).await;
}

trait CallbackWithState<'state, 'arg, State: 'state, Arg: 'arg, Res> {
    type Output: Future<Output = Res>;

    fn call(&self, state: &'state mut State, arg: &'arg mut Arg) -> Self::Output;
}

impl<'state, 'arg, State: 'state, Arg: 'arg, Res, F, Fut>
    CallbackWithState<'state, 'arg, State, Arg, Res> for F
where
    F: Fn(&'state mut State, &'arg mut Arg) -> Fut,
    Fut: Future<Output = Res>,
{
    type Output = Fut;
    fn call(&self, state: &'state mut State, arg: &'arg mut Arg) -> Fut {
        self(state, arg)
    }
}

async fn run_callback_with_state<State, F>(state: &mut State, callback: F)
where
    for<'state, 'arg> F: CallbackWithState<'state, 'arg, State, i32, i32>,
{
    for mut i in 0..10 {
        let res = callback.call(state, &mut i).await;
        println!("{}", res);
    }
}

async fn test_with_state() {
    async fn callback(state: &mut String, x: &mut i32) -> i32 {
        *x += 10;
        state.push_str(&x.to_string());
        println!("{}", x);
        *x + 100
    }
    run_callback_with_state(&mut String::new(), callback).await;
    // Does not work:
    // run_callback_with_state(&mut String::new(), |state: &mut String, x: &mut i32| async move {
    //     *x += 10;
    //     state.push_str(&x.to_string());
    //     println!("{}", x);
    //     let res = *x + 100;
    // });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment