Reputation: 2072
I am trying to build vector of futures using Rust 1.36.0 and futures 0.1.
Future
s?extern crate futures;
pub fn create_some_futures() {
let mapped: Vec<Box<dyn futures::future::Future<Item = i32, Error = ()>>> = (0..10)
.map(|_| Box::new(futures::future::ok(132)))
.collect();
}
fn main() {
create_some_futures();
}
My Cargo.toml
:
[dependencies]
futures = "0.1"
This does not compile:
error[E0277]: a collection of type `std::vec::Vec<std::boxed::Box<dyn futures::Future<Item = i32, Error = ()>>>` cannot be built from an iterator over elements of type `std::boxed::Box<futures::Failed<{integer}, _>>`
--> src/main.rs:6:10
|
6 | .collect();
| ^^^^^^^ a collection of type `std::vec::Vec<std::boxed::Box<dyn futures::Future<Item = i32, Error = ()>>>` cannot be built from `std::iter::Iterator<Item=std::boxed::Box<futures::Failed<{integer}, _>>>`
|
= help: the trait `std::iter::FromIterator<std::boxed::Box<futures::Failed<{integer}, _>>>` is not implemented for `std::vec::Vec<std::boxed::Box<dyn futures::Future<Item = i32, Error = ()>>>`
Why is that? I suspect there's problem converting futures::Finished<{integer}, _>
to <dyn futures::Future<Item = i32, Error = ()>>
because this compiles fine:
pub fn create_some_boxed_ints() {
let mapped: Vec<Box<i32>> = (0..10).map(|_| Box::new(132)).collect();
}
I am not sure what's the problem. The function future::ok
returns FutureResult
which implements Future
, which I expected to be compatible with dyn Future<Item = i32, Error = ()>
.
I am playing with this old version of the futures crate because another project I want to contribute into is using the 0.1 version. I know that the associated type for Future
is Output
in 0.3.x. Maybe I wouldn't have this problem if I switch to more recent version, but I would like to understand the case above to better understand Rust. The error message is the same on 1.39.0.
Upvotes: 1
Views: 2544
Reputation: 30587
Why doesn't the code below work?
I don't think this problem is specific to the futures version you are using - if I update your code to use Future<Output = Result<i32, i32>>
then I get exactly the same result.
The problem here is that your mapping function needs to coerce a concrete future into a trait object.
In the Rust Reference, in the section on type coercions, it describes coercion sites as:
places where the desired type is explicit or can be derived by propagation from explicit types (without type inference).
The context in which your code requires coercion would require type inference - working backwards from the desired container type through the return type of the map
function. It's a bridge too far.
You can overcome it by explicitly converting to the trait object:
Box::new(futures::future::ok(132)) as Box<dyn futures::future::Future<Output = Result<i32, i32>>>
Now it will compile (playground).
To avoid the implicit conversion, you could add a return type to your map
closure:
let mapped: Vec<Box<dyn futures::future::Future<Output = Result<i32, i32>>>> = (0..10)
.map(|_| -> Box<dyn futures::future::Future<Output = Result<i32, i32>>> {
Box::new(futures::future::ok(132))
})
.collect();
This would allow implicit coercion to kick in.
Is there a more idiomatic way to build a list/iterable of Future
s?
That would depend on how the futures were being created and what they would be used for.
You could define a type alias:
type MyDynFuture = Box<dyn futures::future::Future<Output = Result<i32, i32>>>;
so that you could then write:
let mapped: Vec<MyDynFuture> = (0..10)
.map(|_| -> MyDynFuture {
Box::new(futures::future::ok(132))
})
.collect();
Personally I find that a lot more readable.
If there are a number of places in your codebase where you need to manage a collection of futures, you might want to use a function rather than a closure to further reduce the boilerplate.
If the set will contain a limited set of future types, then it may be more efficient to define an enum containing them and collect instances of this enum in the container - this would avoid runtime dispatch and heap allocation.
If the futures are being generated from some iterator, and the
collection is going to be passed to join_all
or some similar
method so you can wait for them all to complete, then you don't need
to collect them at all - for example:
let i = (0..10)
.map(|n| -> futures::future::Ready<Result<i32,i32>> {
futures::future::ok(n)
});
join_all(i)
Caveat: I'm also pretty inexperienced in Rust, there could be other best practices I'm unaware of.
Upvotes: 4