How to find inferred type for variable (FuturesUnordered)

Hello everyone,

I tried to search for similar topics, but maybe was unlucky with my keyword
choice, thus new topic/question.

I need to find an appropriate T to be able to execute let list2: FuturesUnordered<T> = FuturesUnordered::new();.

Compiler infers type and handles it the way i need when i call task_list.push,
the problem is that i want to refactor code in such a way that this functionality
is provided by struct and FuturesUnordered::new() is called in struct builder
and push is called from another function, thus i need to know the T.

It is a given that async fn execute(_: usize) -> (u32, u32) will always be the
same in the same source file.

Does the compiler Box dyn it transparently or there is another type used?
What could i do to deal with this?

Relevant simplified code below.

use futures::{ stream::FuturesUnordered, StreamExt, executor::block_on };

async fn execute(_: usize) -> (u32, u32) {
   /* .. some async code here .. */
   (0, 0)
}

async fn tasks_create() {
   let mut task_list: FuturesUnordered<_> = FuturesUnordered::new();
   /* What is the inferred type for task_list here? */
   task_list.push(execute(2));

   /* like this i can get that type is: opaque impl futures::Future<Output = (u32, u32)> */
   // let provoke_copiler: () = execute(2);

   /* I want to create new empty list2 with correct type without inference,
      but uncommenting line below generates error (rightfully so). */
   // let list2: FuturesUnordered<_> = FuturesUnordered::new();

   while let Some(_) = task_list.next().await {
      /* .. do something .. */
   }
}

fn main() {
   block_on(tasks_create())
}

Returned error for list2 when uncommented:

error[E0698]: type inside `async fn` body must be known in this context
  --> futures-help/src/main.rs:47:16
   |
47 |    let list2 = FuturesUnordered::new();
   |                ^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `Fut`
   |
note: the type is part of the `async fn` body because of this `await`
  --> futures-help/src/main.rs:49:40
   |
49 |    while let Some(_) = task_list.next().await {
   |                                        ^^^^^^

Calling this returns an opaque type impl Future<Output = (u32, u32)>. The compiler doesn't box this.

Unfortunately type aliases for opaque types are currently unstable. So I guess if you can't make inference work (your snippet compiles fine on the playground), you'd have to do the boxing yourself:

use futures::{ stream::FuturesUnordered, StreamExt, executor::block_on, future::BoxFuture };

async fn execute(_: usize) -> (u32, u32) {
   /* .. some async code here .. */
   (0, 0)
}

async fn tasks_create() {
   let mut task_list: FuturesUnordered<BoxFuture<'_, (u32, u32)>> = FuturesUnordered::new();
   
   /* What is the inferred type for task_list here? */
   task_list.push(Box::pin(execute(2)));
   

   /* like this i can get that type is: opaque impl futures::Future<Output = (u32, u32)> */
   // let provoke_copiler: () = execute(2);

   /* I want to create new empty list2 with correct type without inference,
      but uncommenting line below generates error (rightfully so). */
   // let list2: FuturesUnordered<_> = FuturesUnordered::new();

   while let Some(_) = task_list.next().await {
      /* .. do something .. */
   }
}

fn main() {
   block_on(tasks_create())
}

Playground.

Thank you for your prompt reply! Before continuing i wanted to grapple with this myself a bit, this is why i am writing only today. Your answer is correct to the question i had asked, but it turns out i had simplified my example too much, since my async function contains !Send objects at the moment.

So the code (compiles no error) closer to real situation with opaque type would be:

use futures::{ stream::FuturesUnordered, StreamExt, executor::block_on};

async fn execute(_: usize) -> (u32, u32) {
    /* inserting artificial !Send to emulate my case */
    let _x = std::rc::Rc::new(3);

    let _x = futures::future::ready(std::marker::PhantomData::<*const ()>::default).await;

    (0, 0)
}

async fn tasks_create() {
   let mut task_list = FuturesUnordered::new();
   
   task_list.push(execute(2));

   while let Some(_) = task_list.next().await {
      /* .. do something .. */
   }
}

fn main() {
   block_on(tasks_create())
}

If i use BoxFuture version, it does not compile (rightfully so):

use futures::{ stream::FuturesUnordered, StreamExt, executor::block_on, future::BoxFuture };

async fn execute(_: usize) -> (u32, u32) {
    /* inserting artificial !Send to emulate my case */
    let _x = std::rc::Rc::new(3);

    let _y = futures::future::ready(std::marker::PhantomData::<*const ()>::default).await;

    (0, 0)
}

async fn tasks_create() {
   let mut task_list: FuturesUnordered<BoxFuture<'_, (u32, u32)>> = FuturesUnordered::new();
   
   task_list.push(Box::pin(execute(2)));

   while let Some(_) = task_list.next().await {
      /* .. do something .. */
   }
}

fn main() {
   block_on(tasks_create())
}

Because BoxFuture is described as:

pub type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

Of course i can Pin Box it myself without Send requirement:

use futures::{ Future, stream::FuturesUnordered, StreamExt, executor::block_on };
use core::pin::Pin;

async fn execute(_: usize) -> (u32, u32) {
    /* inserting artificial !Send to emulate my case */
    let _x = std::rc::Rc::new(3);

    let _x = futures::future::ready(std::marker::PhantomData::<*const ()>::default).await;
    
    (0, 0)
}

async fn tasks_create() {
   let mut task_list: FuturesUnordered<Pin<Box<dyn Future<Output = (u32, u32)>>>> = FuturesUnordered::new();
   
   task_list.push(Box::pin(execute(2)));

   while let Some(_) = task_list.next().await {
      /* .. do something .. */
   }
}

fn main() {
   block_on(tasks_create())
}

While studying BoxFuture, it has risen an issue with that, that most probably i will want so that my async funciton is Send thus some more code refactoring will have to be done anyways.

Regarding type aliases for opaque types i read the RFC and wanted to play with it in playground (just for curiosity), but i did not see how this actually would help in this case. I guess then i would have to implement Future trait by my self and use different wrapping instead of automatically generated Future from async fn syntax.

You can also use futures::future::LocalBoxFuture

2 Likes

I was thinking something like this:

type Ty = impl Future<Output = (u32, u32)>;

FuturesUnordered::<Ty>::new();

That doesn't work on nightly though, you get an error because the feature currently needs to get the opaque type from the function signature. So async functions, which are implicitly converted into opaque types, aren't supported

Yes, it would be nice if things worked like that. Anyhow, i think i'll go with BoxFuture approach.