I am trying to write a function which reads a response from a server and returns a stream of futures which when awaited should return a type which implements the tokio AsyncRead trait:
pub async fn connect_peers(url: &str) -> impl futures::stream::Stream<Item = impl AsyncRead> {
let response = reqwest::get(url).await.unwrap().bytes().await.unwrap();
let peers: &[u8] = // parse response ...
peers.array_chunks().map(|x: &[u8; 6]| {
let (&ip, port) = x.split_array_ref::<4>();
let addr = SocketAddr::new(IpAddr::from(ip), BigEndian::read_u16(port));
tokio::net::TcpStream::connect(addr)
}).collect()
}
However, I get the following compile error:
error[E0277]: a value of type `impl Stream<Item = impl tokio::io::AsyncRead>` cannot be built from an iterator over elements of type `impl futures::Future<Output = Result<tokio::net::TcpStream, std::io::Error>>`
--> src/lib.rs:350:12
|
350 | }).collect()
| ^^^^^^^ value of type `impl Stream<Item = impl tokio::io::AsyncRead>` cannot be built from `std::iter::Iterator<Item=impl futures::Future<Output = Result<tokio::net::TcpStream, std::io::Error>>>`
|
= help: the trait `FromIterator<impl futures::Future<Output = Result<tokio::net::TcpStream, std::io::Error>>>` is not implemented for `impl Stream<Item = impl tokio::io::AsyncRead>`
I am not sure how to create a futures::stream::Stream from the iterator returned by peers.array_chunks().map(|x: &[u8; 6]| { ... }).
This blog post show that something like this is possible:
fn get_pages_futures() -> impl Stream<Item = impl Future<Output = Vec<usize>>> {
stream::iter(0..).map(|i| get_page(i))
}
where get_page is an async function returning Vec<usize>. But I am not sure how this would translate to my use case.
I have tried the following return type signature (adding the future as output):
impl futures::stream::Stream<Item = impl futures::Future<Output = impl AsyncRead>>
which gives the following compile error:
error[E0277]: a value of type `impl Stream<Item = impl futures::Future<Output = impl tokio::io::AsyncRead>>` cannot be built from an iterator over elements of type `impl futures::Future<Output = Result<tokio::net::TcpStream, std::io::Error>>`
--> src/lib.rs:350:12
|
350 | }).collect()
| ^^^^^^^ value of type `impl Stream<Item = impl futures::Future<Output = impl tokio::io::AsyncRead>>` cannot be built from `std::iter::Iterator<Item=impl futures::Future<Output = Result<tokio::net::TcpStream, std::io::Error>>>`
|
= help: the trait `FromIterator<impl futures::Future<Output = Result<tokio::net::TcpStream, std::io::Error>>>` is not implemented for `impl Stream<Item = impl futures::Future<Output = impl tokio::io::AsyncRead>>`
How do I return an asynchronous stream from the connect_peers function?
The use of stream::iter() is on the right track - it converts an iterator to a stream. Since you already have an iterator, you need to replace xxx.collect() with stream::iter(xxx):
futures::stream::iter(peers.array_chunks().map(|x: &[u8; 6]| {
let (&ip, port) = x.split_array_ref::<4>();
let addr = SocketAddr::new(IpAddr::from(ip), BigEndian::read_u16(port));
tokio::net::TcpStream::connect(addr)
}))
However, that results in an error:
error[E0277]: the trait bound `impl futures::Future<Output = Result<tokio::net::TcpStream, std::io::Error>>: tokio::io::AsyncRead` is not satisfied
If you take a more careful look at the code, the error makes sense: we promised to return a stream of something that implements AsyncRead (such as TcpStream), but connect() doesn't return a TcpStream, it returns a future that will, once it completes, return a TcpStream.
To fix this, you need to turn your stream that returns futures into a stream that returns TcpStreams - something like:
futures::stream::iter(peers.array_chunks().map(|x: &[u8; 6]| {
// ...
tokio::net::TcpStream::connect(addr)
}))
.map(|connect| async { connect.await.unwrap() })
However StreamExt::map() takes an ordinary closure, and we need an async version. The async equivalent of map() is, somewhat surprisingly called then(), and if we change map() to then(), the code compiles:
pub async fn connect_peers(url: &str) -> impl futures::stream::Stream<Item = impl AsyncRead> {
let response = reqwest::get(url).await.unwrap().bytes().await.unwrap();
let peers: &[u8] = b"foo";
futures::stream::iter(peers.array_chunks().map(|x: &[u8; 6]| {
let (&ip, port) = x.split_array_ref::<4>();
let addr = SocketAddr::new(IpAddr::from(ip), BigEndian::read_u16(port));
tokio::net::TcpStream::connect(addr)
}))
.then(|connect| async { connect.await.unwrap() })
}
Playground
Related
I am trying to be comfortable with the idioms of handing Future in Rust.
The idea is that there are two async functions: one's output is another's input.
The first function pairs a bunch of integers along with a bunch of characters.
async fn squeeze_in(elems: [i32;5]) -> Data1 {
let allowed_chars = ['a','b','c','d','e'];
let together = zip(allowed_chars,elems);
let mut data1 = Data1::new();
for _i in together {
data1.append(_i.0,_i.1);
}
data1
}
The other one, takes this list of pairs and gives the the same list but the positions of the elements in a pair, reversed .
async fn blow_out(data1: &Data1) -> Data1Reversed {
let holder: Vec<(i32,char)> = vec![];
let reversed = data1.val1.iter()
.fold(
holder,
| mut accu, next_pair | {
accu.push((next_pair.1,next_pair.0));
accu
}
);
Data1Reversed::new(reversed)
}
The structs Data1 and Data1Reversed are defined, thus:
use std::iter::zip;
use futures::{StreamExt, TryFutureExt};
use futures::FutureExt;
#[derive(Debug)]
struct Data1 {
pub val1: Vec<(char,i32)>
}
impl Data1 {
pub fn new() -> Data1 {
Data1 { val1: vec![] }
}
pub fn append(&mut self, elem_char: char, elem_int: i32) {
self.val1.push((elem_char,elem_int));
}
}
#[derive(Debug)]
struct Data1Reversed {
pub val2: Vec<(i32,char)>
}
impl Data1Reversed {
pub fn new(elems: Vec<(i32,char)>) -> Data1Reversed {
Data1Reversed {
val2: elems
}
}
}
This arrangement, where two functions are called one after the other, after awaiting, works.
However, I want to arrange the flow in a continuation, thus:
let squeezed_and_blown = squeeze_in(some_num).and_then(move |data1| blow_out(&data1)) ;
My understanding is that, each of the functions being async, each produces a future. A flow like a future1.and_then(future2) should be permissible. The compiler doesn't think so, though:
error[E0599]: the method `and_then` exists for opaque type `impl futures::Future<Output = Data1>`, but its trait bounds were not satisfied
--> src/main.rs:38:51
|
38 | let squeezed_and_blown = squeeze_in(some_num).and_then(|data1| blow_out(&data1) ) ;
| ^^^^^^^^ method cannot be called on `impl futures::Future<Output = Data1>` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`impl futures::Future<Output = Data1>: TryFuture`
which is required by `impl futures::Future<Output = Data1>: TryFutureExt`
`&impl futures::Future<Output = Data1>: TryFuture`
which is required by `&impl futures::Future<Output = Data1>: TryFutureExt`
`&mut impl futures::Future<Output = Data1>: TryFuture`
which is required by `&mut impl futures::Future<Output = Data1>: TryFutureExt`
I have visited this conversation, if I get an hint of where I am going wrong, but that is not the case. The problem in my case, is with the first future and not what is provided to and_then.
What is confusing me is this message from the compiler:
error[E0599]: the method `and_then` exists for opaque type `impl futures::Future<Output = Data1>`, but its trait bounds were not satisfied
What is it unhappy about?
In response to #Jmb's comment below, here is how I want to use the functions:
// Here, a single future is used. However, the actual functionality requires a set of
// futures to be launched.
let maybe_reveresed =
tokio::spawn({
squeeze_in(some_num)
.and_then(|data1| { blow_out(&data1) })
// and, I can add many such operations, serially here.
}
);
// Here again, the actual functionality requires us to join a number of futures obtained earlier.
let have_we_got_it_reversed = maybe_reveresed.await;
// .. rest of implementation.
I'm trying to read a CSV file using the CSV crate and lazily cast all its cells to f64:
use csv::ReaderBuilder; // 1.1.4
fn get_df_iterator(path: &str) {
// Build the CSV reader and iterate over each record casting to f64
let rdr = ReaderBuilder::new()
.delimiter(b'\t')
.from_path(path)
.unwrap();
rdr.into_records().map(|record_result| {
record_result
.unwrap()
.into_iter()
.map(|cell: &str| cell.parse::<f64>().unwrap())
})
}
fn main() {
let m1 = get_df_iterator("df1.csv");
}
playground
I've read that to return types with closures in stable Rust we can use Boxes. I've tried with:
use csv::{Error, ReaderBuilder, StringRecord};
fn get_df_iterator_box(path: &str) -> Box<dyn Iterator<Item = Box<dyn Iterator<Item = f64>>>> {
// Build the CSV reader and iterate over each record.
let rdr = ReaderBuilder::new()
.delimiter(b'\t')
.from_path(path)
.unwrap();
// Returns Box<Iterator<Item = f64>>
let parse_str = |cell: &str| cell.parse::<f64>().unwrap();
let parse_record = |record_result: Result<StringRecord, Error>| {
Box::new(record_result.unwrap().into_iter().map(parse_str))
};
// Box<Iterator<Item = Box<Iterator<Item = f64>>>>
Box::new(rdr.into_records().map(parse_record))
}
fn main() {
let m1 = get_df_iterator_box("df1.csv");
}
error[E0271]: type mismatch resolving `<[closure#src/main.rs:13:24: 15:6] as FnOnce<(std::result::Result<StringRecord, csv::Error>,)>>::Output == Box<(dyn Iterator<Item = f64> + 'static)>`
--> src/main.rs:18:5
|
11 | let parse_str = |cell: &str| cell.parse::<f64>().unwrap();
| ----------------------------------------- the expected closure
...
18 | Box::new(rdr.into_records().map(parse_record))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `Map`, found trait object `dyn Iterator`
|
= note: expected struct `Box<Map<StringRecordIter<'_>, [closure#src/main.rs:11:21: 11:62]>>`
found struct `Box<(dyn Iterator<Item = f64> + 'static)>`
= note: required because of the requirements on the impl of `Iterator` for `Map<StringRecordsIntoIter<File>, [closure#src/main.rs:13:24: 15:6]>`
= note: required for the cast to the object type `dyn Iterator<Item = Box<(dyn Iterator<Item = f64> + 'static)>>`
I don't understand what the problem is. It's supposed to return a Box which contains an iterator that returns Boxes of iterators of f64. Here there's another playground with that update! If you want to replicate with the same file I'm using here you can download it.
You can specify the return type of the closure to tell Rust that you are returning a dyn trait:
let parse_record =
|record_result: Result<StringRecord, Error>| -> Box<dyn Iterator<Item = f64>> {
Box::new(record_result.unwrap().into_iter().map(parse_str))
};
An alternative method would be to explicitly cast within the closure:
let parse_record = |record_result: Result<StringRecord, Error>| {
Box::new(record_result.unwrap().into_iter().map(parse_str)) as Box<dyn Iterator<Item = f64>>
};
You could instead return impl trait instead of using dynamic dispatch and avoid this altogether:
fn get_iterator(path: &str) -> impl Iterator<Item = impl Iterator<Item = f64>> { ... }
For more information about returning traits, see What is the correct way to return an Iterator?
I need to implement a trait that is returning the futures::StreamExt trait.
In general this sounds easy and there are several answers to this e.g. this here.
I tried this with StreamExt but this does - for some reason - not work. Here my sample code:
// [dependencies]
// futures = "0.3.6"
// async-std = { version = "1.6.5", features = ["attributes", "unstable"] } // interval function is unstable atm.
use std::time::Duration;
use futures::StreamExt;
trait StreamProvidingTrait {
fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>>;
}
struct StreamProvider {}
impl StreamProvidingTrait for StreamProvider {
fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>> {
return Box::new(async_std::stream::interval(Duration::from_millis(1000)).map(|_| 1));
}
}
#[async_std::main]
async fn main() {
let mut counter = 0;
let object = StreamProvider {};
// creates a stream
let worx = object.returnastream();
// mappes the stream into something....
let mut mapped_stream = worx.map(|_| {
counter = counter + 1;
counter
});
// subscribing to the items
while let item = mapped_stream.next().await {
match item {
Some(value) => println!("{}", value),
_ => {}
}
}
}
And here the error:
Compiling traittest v0.1.0 (/Users/andre/repos/traittest)
warning: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:1326:8
|
1326 | fn poll_next_unpin(&mut self, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>
| ^^^^^^^^^^^^^^^ the trait cannot be made into an object because method `poll_next_unpin` references the `Self` type in its `where` clause
|
= note: `#[warn(where_clauses_object_safety)]` on by default
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #51443 <https://github.com/rust-lang/rust/issues/51443>
error[E0038]: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> src/main.rs:6:36
|
6 | fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
|
::: /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:226:8
|
226 | fn next(&mut self) -> Next<'_, Self>
| ---- the trait cannot be made into an object because method `next` references the `Self` type in its return type
...
976 | fn by_ref(&mut self) -> &mut Self {
| ------ the trait cannot be made into an object because method `by_ref` references the `Self` type in its return type
...
1381 | fn select_next_some(&mut self) -> SelectNextSome<'_, Self>
| ---------------- the trait cannot be made into an object because method `select_next_some` references the `Self` type in its return type
error[E0038]: the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
--> src/main.rs:12:36
|
12 | fn returnastream(self: &Self) -> Box<dyn StreamExt<Item=i32>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `futures_util::stream::stream::StreamExt` cannot be made into an object
|
::: /Users/andre/.cargo/registry/src/github.com-1ecc6299db9ec823/futures-util-0.3.6/src/stream/stream/mod.rs:226:8
|
226 | fn next(&mut self) -> Next<'_, Self>
| ---- the trait cannot be made into an object because method `next` references the `Self` type in its return type
...
976 | fn by_ref(&mut self) -> &mut Self {
| ------ the trait cannot be made into an object because method `by_ref` references the `Self` type in its return type
...
1381 | fn select_next_some(&mut self) -> SelectNextSome<'_, Self>
| ---------------- the trait cannot be made into an object because method `select_next_some` references the `Self` type in its return type
error: aborting due to 2 previous errors; 1 warning emitted
For more information about this error, try `rustc --explain E0038`.
error: could not compile `traittest`.
To learn more, run the command again with --verbose.
Process finished with exit code 101
When I exchange the StreamExt trait with my own one, this code works perfectly.
trait SomeTrait {
fn returnatrait(self: &Self) -> Box<dyn SomeOtherTrait>;
}
trait SomeOtherTrait {
fn sayHelloWorld(&self);
}
struct DummyStruct {}
impl SomeOtherTrait for DummyStruct {
fn sayHelloWorld(&self) {
println!("hello world");
}
}
struct Implementation {}
impl SomeTrait for Implementation {
fn returnatrait(self: &Self) -> Box<dyn SomeOtherTrait> {
return Box::new(DummyStruct{});
}
}
fn main() {
let implementation = Implementation{};
let worx = implementation.returnatrait();
worx.sayHelloWorld();
}
What's wrong here? There's obviously something I don't understand. Please help me understand this!
A function returning a trait can use the impl Trait syntax to return an opaque type that implements the trait. A trait method returning a trait currently doesn't support this feature and requires the trait to be returned as a trait object - a dynamically dispatched reference or smart pointer such as Box or Rc. Not all traits are object-safe, though, and the bad news is that StreamExt is among those that aren't, for reasons pointed out by the compiler, such as referencing Self in method return types and where clauses.
The good news, however, is that this is not a problem: StreamExt is an extension trait, one that provides a blanket implementation for all types that implement Stream. So you don't need to bother returning a dyn StreamExt trait object, you can return a dyn Stream one, and you'll still get access to StreamExt methods simply by requesting them with use StreamExt. In other words, you can just replace Box<dyn StreamExt> with Box<dyn Stream> in the return type of your trait.
Another issue you might encounter is that Box<dyn Stream> doesn't work on methods that need to move the stream, which includes many methods provided by StreamExt. Those will require the stream to be pinned, which can be fixed by returning Pin<Box<dyn Stream>> instead of Box<dyn Stream>. There is even a boxed() method on StreamExt that pins and boxes the stream in one operation; code that uses it would look like this (playground):
use futures::stream::{Stream, StreamExt};
use std::pin::Pin;
trait StreamProvidingTrait {
fn returnastream(&self) -> Pin<Box<dyn Stream<Item = i32>>>;
}
struct StreamProvider {}
impl StreamProvidingTrait for StreamProvider {
fn returnastream(&self) -> Pin<Box<dyn Stream<Item = i32>>> {
return tokio::stream::once(0).boxed();
}
}
fn main() {
let provider = StreamProvider {};
let stream = provider.returnastream();
let _fut = stream.into_future();
}
I'm new to Rust and I'm sorry if I'm using terms incorrect. Maybe my choice of words for question is incorrect.
I was playing with streams and I needed to have some delay between stream elements. So I wrote this:
use futures::stream;
use futures::StreamExt;
use tokio::time;
#[tokio::main]
async fn main() {
let mut stream = stream::iter(0..1000).then(|x| async move {
time::delay_for(std::time::Duration::from_millis(500)).await;
x + 1
});
while let Some(x) = stream.next().await {
println!("{:?}", x)
}
}
I get a lot of compilation errors, but the most important errors are connected with pinning. Here they are:
error[E0277]: `std::future::from_generator::GenFuture<[static generator#src/main.rs:7:64: 10:6 x:_ _]>` cannot be unpinned
--> src/main.rs:11:32
|
11 | while let Some(x) = stream.next().await {
| ^^^^ within `futures_util::stream::stream::then::_::__Then<'_, futures_util::stream::iter::Iter<std::ops::Range<{integer}>>, impl core::future::future::Future, [closure#src/main.rs:7:49: 10:6]>`, the trait `std::marker::Unpin` is not implemented for `std::future::from_generator::GenFuture<[static generator#src/main.rs:7:64: 10:6 x:_ _]>`
|
= note: required because it appears within the type `impl core::future::future::Future`
= note: required because it appears within the type `std::option::Option<impl core::future::future::Future>`
= note: required because it appears within the type `futures_util::stream::stream::then::_::__Then<'_, futures_util::stream::iter::Iter<std::ops::Range<{integer}>>, impl core::future::future::Future, [closure#src/main.rs:7:49: 10:6]>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `futures_util::stream::stream::then::Then<futures_util::stream::iter::Iter<std::ops::Range<{integer}>>, impl core::future::future::Future, [closure#src/main.rs:7:49: 10:6]>`
error[E0277]: `std::future::from_generator::GenFuture<[static generator#src/main.rs:7:64: 10:6 x:_ _]>` cannot be unpinned
--> src/main.rs:11:25
|
11 | while let Some(x) = stream.next().await {
| ^^^^^^^^^^^^^^^^^^^ within `futures_util::stream::stream::then::_::__Then<'_, futures_util::stream::iter::Iter<std::ops::Range<{integer}>>, impl core::future::future::Future, [closure#src/main.rs:7:49: 10:6]>`, the trait `std::marker::Unpin` is not implemented for `std::future::from_generator::GenFuture<[static generator#src/main.rs:7:64: 10:6 x:_ _]>`
|
= note: required because it appears within the type `impl core::future::future::Future`
= note: required because it appears within the type `std::option::Option<impl core::future::future::Future>`
= note: required because it appears within the type `futures_util::stream::stream::then::_::__Then<'_, futures_util::stream::iter::Iter<std::ops::Range<{integer}>>, impl core::future::future::Future, [closure#src/main.rs:7:49: 10:6]>`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `futures_util::stream::stream::then::Then<futures_util::stream::iter::Iter<std::ops::Range<{integer}>>, impl core::future::future::Future, [closure#src/main.rs:7:49: 10:6]>`
= note: required because of the requirements on the impl of `core::future::future::Future` for `futures_util::stream::stream::next::Next<'_, futures_util::stream::stream::then::Then<futures_util::stream::iter::Iter<std::ops::Range<{integer}>>, impl core::future::future::Future, [closure#src/main.rs:7:49: 10:6]>>`
If I change my code to this:
use futures::stream;
use futures::StreamExt;
use tokio::time;
#[tokio::main]
async fn main() {
let mut stream = stream::iter(0..1000).then(|x| {
futures::future::ready(x + 1)
});
while let Some(x) = stream.next().await {
println!("{:?}", x)
}
}
Or to this:
use futures::stream;
use futures::StreamExt;
use tokio::time;
#[tokio::main]
async fn main() {
stream::iter(0..1000)
.then(|x| async move {
time::delay_for(std::time::Duration::from_millis(500)).await;
x + 1
})
.for_each(|x| async move { println!("{:?}", x) })
.await;
}
It compiles.
I assume it has something to do with pinning and simultaneous usage of then combinator and while, but I can't wrap my head around it.
I think that the issue boils down to the fact that async blocks are not Unpin. It can be proven with this code:
fn check_unpin<F: Unpin>(_: F) { }
fn main() {
check_unpin(async {});
}
that fails with the rather cryptic message:
error[E0277]: `std::future::from_generator::GenFuture<[static generator#src/main.rs:4:23: 4:25 _]>` cannot be unpinned
--> src/main.rs:4:5
|
1 | fn check_unpin<F: Unpin>(_: F) { }
| ----- required by this bound in `check_unpin`
...
4 | check_unpin(async {});
| ^^^^^^^^^^^ within `impl std::future::Future`, the trait `std::marker::Unpin` is not implemented for `std::future::from_generator::GenFuture<[static generator#src/main.rs:4:23: 4:25 _]>`
|
= note: required because it appears within the type `impl std::future::Future`
I reckon that GenFuture is the internal type that converts an async block into a impl Future.
Now back to your issue, the combinator then() returns an Unpin value if both the stream and the Future returned by the closure are Unpin (it is not totally clear in the documentation, but I've inferred that from the source code). The stream::iter is Unpin, but when you write |x| async move { x + 1} you are returning an async block that is not Unpin, thus your error.
If you use futures::future::ready(x + 1) it works simply because that Future implements Unpin.
If you use StreamExt::for_each it works because it does not require Self to be Unpin. It is not Unpin itself, but it does not matter because you are sending it up to tokio::main that pins everything internally before polling.
If you want your original code to work you just have to manually pin your stream (playground):
use futures::stream;
use futures::StreamExt;
use tokio::time;
use pin_utils::pin_mut;
#[tokio::main]
async fn main() {
let stream = stream::iter(0..1000).then(|x| async move {
time::delay_for(std::time::Duration::from_millis(500)).await;
x + 1
});
pin_mut!(stream); //<---- here, pinned!
while let Some(x) = stream.next().await {
println!("{:?}", x)
}
}
I'm unable to work out how to resolve the compilation error resulting from adapting a stream using a combinator.
The following Rust Playground demonstrates a fairly minimal example:
use futures::prelude::*;
use futures::StreamExt;
#[derive(Debug)]
pub enum Input {
A,
B(i32),
C(u16),
}
#[derive(Debug)]
enum Output {
Int(i32),
Short(u16),
}
pub struct StreamMaker;
impl StreamMaker {
/// make a stream with a series of inputs
pub fn create(self) -> impl Stream<Item = Input> {
stream::iter(vec![Input::A, Input::C(1u16), Input::B(2)])
}
}
/// consume the creator, and make output messages for a subset
pub fn adapt_stream(creator: StreamMaker) -> impl Stream<Item = Output> {
let mut upstream = creator.create();
upstream.filter_map(|message| async move {
match message {
Input::A => None,
Input::B(v) => Some(Output::Int(v)),
Input::C(v) => Some(Output::Short(v)),
}
})
}
#[tokio::main]
async fn main() -> Result<(), ()> {
let creator = StreamMaker {};
let mut stream = adapt_stream(creator);
while let Some(message) = stream.next().await {
println!("message: {:?}", message)
}
Ok(())
}
Compilation fails with:
error[E0277]: the trait bound `std::future::GenFuture<[static generator#src/main.rs:29:46: 35:6 message:Input {}]>: std::marker::Unpin` is not satisfied in `impl core::future::future::Future`
--> src/main.rs:43:38
|
43 | while let Some(message) = stream.next().await {
| ^^^^ within `impl core::future::future::Future`, the trait `std::marker::Unpin` is not implemented for `std::future::GenFuture<[static generator#src/main.rs:29:46: 35:6 message:Input {}]>`
|
= help: the following implementations were found:
<std::future::GenFuture<T> as std::marker::Unpin>
= note: required because it appears within the type `impl core::future::future::Future`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `futures_util::stream::stream::filter_map::FilterMap<impl futures_core::stream::Stream, impl core::future::future::Future, [closure#src/main.rs:29:25: 35:6]>`
= note: required because it appears within the type `impl futures_core::stream::Stream`
error[E0277]: the trait bound `std::future::GenFuture<[static generator#src/main.rs:29:46: 35:6 message:Input {}]>: std::marker::Unpin` is not satisfied in `impl core::future::future::Future`
--> src/main.rs:43:31
|
43 | while let Some(message) = stream.next().await {
| ^^^^^^^^^^^^^^^^^^^ within `impl core::future::future::Future`, the trait `std::marker::Unpin` is not implemented for `std::future::GenFuture<[static generator#src/main.rs:29:46: 35:6 message:Input {}]>`
|
= help: the following implementations were found:
<std::future::GenFuture<T> as std::marker::Unpin>
= note: required because it appears within the type `impl core::future::future::Future`
= note: required because of the requirements on the impl of `std::marker::Unpin` for `futures_util::stream::stream::filter_map::FilterMap<impl futures_core::stream::Stream, impl core::future::future::Future, [closure#src/main.rs:29:25: 35:6]>`
= note: required because it appears within the type `impl futures_core::stream::Stream`
= note: required because of the requirements on the impl of `core::future::future::Future` for `futures_util::stream::stream::next::Next<'_, impl futures_core::stream::Stream>`
I can put a pin_mut!(stream); in the main, but I'd like to be able to push that upstream.
If you do not want the consumer of your stream to have to pin it themselves, you need to return a stream that implements the Unpin trait, meaning that it is safe to move around in memory even after is has been pinned.
pub fn adapt_stream(creator: StreamMaker) -> impl Stream<Item = Output> + Unpin {
// add Unpin trait --^
Adding this, your compiler should complain that the return value doesn't implement Unpin. This is because async move { ... } blocks don't implement Unpin, since they may be self-referential (e.g. contain references to variables they own). The most general way to work around this is to pin the stream to the heap with a Pin<Box<_>>, using the Box::pin constructor:
pub fn adapt_stream(creator: StreamMaker) -> impl Stream<Item = Output> + Unpin {
let mut upstream = creator.create();
Box::pin(upstream.filter_map(|message| async move {
// ^-- pin stream to heap
match message {
Input::A => None,
Input::B(v) => Some(Output::Int(v)),
Input::C(v) => Some(Output::Short(v)),
}
}))
}
Since we're now returning a Pin<Box<_>> pointer to the stream, that pointer can be safely moved around in memory while the inner stream is kept at the same location.
Full playground example