What is the concrete type of a future returned from `async fn`?
Asked Answered
I

1

15

What type should I use for a vector that stores futures?

I tried to make multiple concurrent requests on the same URL and save all the futures into the vector to use with join_all.

If I don't set a type for the vector explicitly, everything works. I understand that Rust can find the proper type of a variable. CLion determines the vector type as Vec<dyn Future<Output = ()>>, but when I try to set the type by myself, it gives me an error:

error[E0277]: the size for values of type `dyn core::future::future::Future<Output = ()>` cannot be known at compilation time
  --> src/lib.rs:15:23
   |
15 |     let mut requests: Vec<dyn Future<Output = ()>> = Vec::new();
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `dyn core::future::future::Future<Output = ()>`
   = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
   = note: required by `std::vec::Vec`

I must declare the type as Vec<Pin<Box<dyn Future<Output=()>>>> which forces me to wrap result of function into requests.push(Pin::from(Box::new(request(i))));

use futures::future::join_all;
use std::future::Future;
use std::pin::Pin;

async fn request(n: i32) {
    println!("Started: {}", n);
    let response = reqwest::get("https://www.rust-lang.org")
        .unwrap()
        .text()
        .unwrap();
    println!("Completed: {}. Response: {}", n, &response[0..10]);
}

async fn main() {
    let mut requests: Vec<dyn Future<Output = ()>> = Vec::new();
    for i in 0..5 {
        requests.push(request(i));
    }
    join_all(requests).await;
}

Which type it should be?

Interstitial answered 8/11, 2019 at 7:28 Comment(5)
Vec<dyn Future<Output=()>> is not a valid type since it's size unknown at compile time. It is a type hint to represent Opaque type which returns from async fn request(n: i32) {}. It might be a better choice if it displays like that Vec<impl Future<Output=()>>, but still this is not a valid type. Consider this type as T which implements Future<Output=()>. Also you don't need to Pin Box your Futures unless your opaque types are different : please seePluralize
So if you still want to declare type explicitly you can do at least let mut requests:Vec<_> = Vec::new();Pluralize
I still don't understand, how to Rust recognize a value type if i do not declare it explicitly? And why i can't write it?Interstitial
"async/.await is Rust's built-in tool for writing asynchronous functions that look like synchronous code. async transforms a block of code into a state machine that implements a trait called Future" (Please see: async await primer)Pluralize
Sorry i read that more than three time, but still do not understand. Rust transform async block into state machine, but it's state machine working around some struct and it's struct can be a some type with can be set to a variable. Or it's just a some rust's magic kind and i don't care about this, becose i can't do some with it? And the best i can is let Rust take care about variables types.Interstitial
C
9

From the RFC:

The return type of an async function is a unique anonymous type generated by the compiler, similar to the type of a closure. You can think of this type as being like an enum, with one variant for every "yield point" of the function - the beginning of it, the await expressions, and every return. Each variant stores the state that is needed to be stored to resume control from that yield point.

When the function is called, this anonymous type is returned in its initial state, which contains all of the arguments to this function.

You can't explicitly declare the concrete type of a future since it is an anonymous type. As an API user we only need to know that it implements std::futures::Future but this doesn't mean that we don't need a deeper knowledge of this anonymous type and it's implementation, it would be nice to have for grasping the concept.


CLion determines the vector type as Vec<dyn Future<Output = ()>>

This is a type hint, not the actual type, since compiler is not able to know the size of dyn Future<Output = ()>, it will not be compiled.


Pin<Box<_>>-ing a Future to declare an explicit type might not be a good idea. In your case it's not needed because the concrete types returned from async fn are identical. Letting the compiler infer the type will just be fine.

See also:

Cepheus answered 8/11, 2019 at 12:56 Comment(2)
could you give an example how to declare concrete type without Pin<Box<_>>?Accompany
@Accompany as I pointed in the answer; returned Future from async fn is an anonymous type, it is generated by compiler, you cannot declare since you don't know what compiler will generate. What kind of situation you are dealing with, if you give me an example, I can try to help ?Pluralize

© 2022 - 2024 — McMap. All rights reserved.