Futures: loop_fn vs map

The following example doesn't compile, and I can't figure out what compiler said to me.

extern crate futures;
use futures::future::{loop_fn, ok, Future, Loop};

fn main() {
    let mut r = loop_fn(3, |x| {
        if x == 0 {
            ok::<_, ()>(x).map(|i| Loop::Break(i))
        } else {
            ok::<_, ()>(x -1).map(|i| Loop::Continue(i))
        }
    });
    println!("{:?}", r.poll());
}

(Playground)

error[E0308]: if and else have incompatible types
  --> src/main.rs:6:9
   |
6  | /         if x == 0 {
7  | |             ok::<_, ()>(x).map(|i| Loop::Break(i))
8  | |         } else {
9  | |             ok::<_, ()>(x -1).map(|i| Loop::Continue(i))
10 | |         }
   | |_________^ expected closure, found a different closure
   |
   = note: expected type `futures::Map<_, [closure@src/main.rs:7:32: 7:50]>`
              found type `futures::Map<_, [closure@src/main.rs:9:35: 9:56]>`
   = note: no two closures, even if identical, have the same type
   = help: consider boxing your closure and/or using it as a trait object

error: aborting due to previous errorno two closures, even if identical, have the same type

It works well if I replace "ok(x).map(|i| Loop::...(i))" with just "ok(Loop::...(x))", of course.

The closure given to loop_fn must return a single concrete type. As part of the error message says:

there are two separate closures used, and each one causes a different type to be generated.

I’m not sure what your real case looks like but you can sink that logic into the inner closure instead: example

According to my real code I could get rid of one closure, but still need another future with .map() like that:

loop_fn(3, |x| {
  if x == 0 {
    ok::<_, ()>(Loop::Break(x))
  } else {
    ok::<_, ()>(x - 1).map(|i| Loop::Continue(i))
  }
})

But compiler doesn't eat it:

expected struct `futures::FutureResult`, found struct `futures::Map`

or in more details:

expected type `futures::FutureResult<futures::future::Loop<{integer}, _>, _>`
   found type `futures::Map<futures::FutureResult<{integer}, _>, [closure@src/main.rs:9:36: 9:62]>`

Is it possible to make compiler to infer some common type like impl Future or dyn IntoFuture?

It looks like the closure doesn't capture any context. So, instead of using closure, you can use functions like this:

extern crate futures;
use futures::future::{loop_fn, ok, Future, Loop};

fn main() {
    fn zero_func(i: i32) -> futures::future::Loop<i32, i32> 
    { 
        Loop::Break(i)
    }
    
    fn nonzero_func(i: i32) -> futures::future::Loop<i32, i32>
    { 
        Loop::Continue(i)
    }

    let mut r = loop_fn(3, |x| {
        if x == 0 {
            let res : futures::Map<_, fn(i32) -> futures::future::Loop<_, i32>> = ok::<_, ()>(x).map(zero_func);
            res
        } else {
            let res : futures::Map<_, fn(i32) -> futures::future::Loop<_, i32>> = ok::<_, ()>(x -1).map(nonzero_func);
            res
        }
    });
    println!("{:?}", r.poll());
}

Playground

Because static function of the same signature have equal type, this works. I don't know exactly why the tempory variable is neccesairy (to specify the type), but I guess there was just a bit too much type inteference.

You can also try using Either in your real code: example

Oh, great! It works well even if one branch contains .map whereas another branch doesn't. Thanks a lot!