-1

Is it possible to specify a generic lifetime on FnMut in Rust for a function like this?

fn f1<'a, FRet:'a + FnMut() -> ()>(v: &'a mut i32) -> FRet {
   let mut fret = || {
       let i = v;
       println!("inside");
       // lots of complicated logic on captured var
       *i = 5;
   };

   return fret;
}

Right now I'm getting an error, that return is incompatible with FRet.

Ivan Koshelev
  • 3,830
  • 2
  • 30
  • 50
  • Does this answer your question? [Returning a closure from a function](https://stackoverflow.com/questions/25445761/returning-a-closure-from-a-function) – Chayim Friedman Feb 14 '22 at 22:57

1 Answers1

3

Your code has several issues. One is that you cannot make the function that returns a closure have a generic return type. f1 being generic over FRet means that the caller gets to choose the FRet, and that obviously can't work when you're returning a closure you implement, and not something the caller to specify. This is why the compiler complains that FRet is incompatible with what you're actually returning.

The way to return a closure is by either boxing it or using the impl Trait return type that declares an unnamed type returned by the function:

fn f1(v: &mut i32) -> impl FnMut() {
   let fret = || {
       let i = v;
       println!("inside");
       // lots of complicated logic on captured var
       *i = 5;
   };

   fret
}

The above still doesn't compile with the compiler complaining that the closure is FnOnce rather than FnMut. This is because &mut i32 is not Copy, so let i = v actually moves the reference out of v which makes the closure callable only once. That can be fixed with a reborrow, i.e. changing let i = v to let i = &mut *v.

After that the compiler will complain that the lifetime of reference is not captured by the closure, and that you can fix that by adding + '_. (This is equivalent to declaring a lifetime 'a and declaring v: &'a mut i32 and impl FnMut() + 'a.) With this change the code compiles:

fn f1(v: &mut i32) -> impl FnMut() + '_ {
   let fret = || {
       let i = &mut *v;
       println!("inside");
       // lots of complicated logic on captured var
       *i = 5;
   };

   fret
}

Playground

user4815162342
  • 141,790
  • 18
  • 296
  • 355
  • Note the explicit reborrow isn't necessary, we just have to use `*v` instead of introducing `i`, and rust will automatically reborrow. – cdhowie Feb 15 '22 at 10:04
  • @cdhowie Good point. The answer assumes the OP had good reason to introduce `i`, which may not in fact be the case. – user4815162342 Feb 15 '22 at 10:48