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!