How can I return a vector of responses from a set of concurrent GET requests? - asynchronous

I'm just getting started with Rust and am trying to work with concurrent requests. My aim is to have an asynchronous function that returns a vector of responses from a number of GET requests. What I have currently does successfully execute the requests concurrently but does not return anything from the function:
// main.rs
mod api_requester;
#[tokio::main]
async fn main() {
let values = vec!["dog".to_string(), "cat".to_string(), "bear".to_string()];
let responses = api_requester::get_data(values).await;
println!("{:?}", responses)
// do more stuff with responses
}
// api_requester.rs
use serde::Deserialize;
use futures::{stream, StreamExt};
use reqwest::Client;
const CONCURRENT_REQUESTS: usize = 2;
const API_ENDPOINT: &str = "https://httpbin.org/response-headers";
#[derive(Deserialize, Debug)]
struct ApiResponse {
#[serde(rename(deserialize = "Content-Length"))]
content_length: String,
#[serde(rename(deserialize = "Content-Type"))]
content_type: String,
freeform: String
}
pub async fn get_data(values: Vec<String>) {
let client = Client::new();
let bodies = stream::iter(values)
.map(|value| {
let client = &client;
async move {
let resp = client.get(API_ENDPOINT)
.query(&[("freeform", value)])
.send().await?;
resp.json::<ApiResponse>().await
}
})
.buffer_unordered(CONCURRENT_REQUESTS);
bodies
.for_each(|body| async {
match body {
Ok(body) => println!("Got {:?}", body),
Err(e) => eprintln!("Got an error: {:?}", e),
}
})
.await;
}
My goal is to return a vector of the responses received from the GET requests back to the main function for further use. But this is where I'm having some serious confusion. I thought I would be able to just await the value in the function and return the vector when the futures have been resolved. something like this:
// api_requester.rs
...
pub async fn get_data(values: Vec<String>) -> Vec<ApiResponse> {
let client = Client::new();
let bodies = stream::iter(values)
.map(|value| {
let client = &client;
async move {
let resp = client.get(API_ENDPOINT)
.query(&[("freeform", value)])
.send().await?;
resp.json::<ApiResponse>().await
}
})
.buffer_unordered(CONCURRENT_REQUESTS);
bodies
}
This produces the following error:
error[E0308]: mismatched types
--> src/api_requester.rs:37:5
|
24 | .map(|value| {
| ------- the found closure
...
27 | async move {
| ________________________-
28 | | let resp = client.get(API_ENDPOINT)
29 | | .query(&[("freeform", value)])
30 | | .send().await?;
31 | |
32 | | resp.json::<ApiResponse>().await
33 | | }
| |_____________- the found `async` block
...
37 | bodies
| ^^^^^^ expected struct `Vec`, found struct `BufferUnordered`
|
::: /home/seraub/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/future/mod.rs:72:43
|
72 | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected struct `Vec<ApiResponse>`
found struct `BufferUnordered<futures::stream::Map<futures::stream::Iter<std::vec::IntoIter<std::string::String>>, [closure#src/api_requester.rs:24:14: 24:21]>>`
For more information about this error, try `rustc --explain E0308`.
I'm guessing that the BufferUnordered<futures::stream::Map<futures::stream::Iter<std::vec::IntoIter<std::string::String>>, [closure#src/api_requester.rs:24:14: 24:21]>> struct found by the compiler needs to be realized/completed?
How can I turn this BufferUnordered object into a simple vector of responses and return it back to the main function?

Related

How do I borrow a mutable reference from an Arc<Mutex<T>> and call an async member function on it?

I have a simple struct and an implementation that looks like this.
#[derive(Debug)]
struct MyStruct {
data: u64,
}
impl MyStruct {
async fn something_async(&mut self) -> Result<(), Box<dyn Error>> {
self.data += 1;
Ok(())
}
}
I want to use MyStruct on the heap using a smart pointer and a mutex so that I can use it from multiple threads.
However, when I try to call the async function called something_async()...
tokio::spawn(async move {
let ptr = Arc::new(Mutex::new(MyStruct { data: 1 }));
let mut s = ptr.lock().unwrap();
s.something_async().await.unwrap();
println!("{:?}", s);
});
...I get the following error:
error: future cannot be sent between threads safely
--> src/main.rs:18:5
|
18 | tokio::spawn(async move {
| ^^^^^^^^^^^^ future created by async block is not `Send`
|
= help: within `impl Future<Output = [async output]>`, the trait `Send` is not implemented for `std::sync::MutexGuard<'_, MyStruct>`
note: future is not `Send` as this value is used across an await
--> src/main.rs:21:9
|
20 | let mut s = ptr.lock().unwrap();
| ----- has type `std::sync::MutexGuard<'_, MyStruct>` which is not `Send`
21 | s.something_async().await.unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^ await occurs here, with `mut s` maybe used later
22 | println!("{:?}", s);
23 | });
| - `mut s` is later dropped here
note: required by a bound in `tokio::spawn`
--> /playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.16.1/src/task/spawn.rs:127:21
|
127 | T: Future + Send + 'static,
| ^^^^ required by this bound in `tokio::spawn`
I am assuming that the compiler does not like me keeping a MutexGuard held across an await, so I tried this instead:
let fut = {
let mut s = ptr.lock().unwrap();
s.something_async()
};
fut.await.unwrap();
But of course then it complains about the Future outliving the mutable reference not living long enough:
error[E0597]: `s` does not live long enough
--> src/main.rs:22:13
|
20 | let fut = {
| --- borrow later stored here
21 | let mut s = ptr.lock().unwrap();
22 | s.something_async()
| ^^^^^^^^^^^^^^^^^^ borrowed value does not live long enough
23 | };
| - `s` dropped here while still borrowed
How do I call an async method on an object wrapped in an Arc<Mutex<T>>?
Make MyStruct copyable.
#[derive(Debug, Copy, Clone)]
struct MyStruct {
data: u64,
}
impl MyStruct {
async fn something_async(&mut self) -> Result<(), Box<dyn Error>> {
self.data += 1;
Ok(())
}
}
#[tokio::main]
async fn main() {
tokio::spawn(async move {
let ptr = Arc::new(Mutex::new(MyStruct { data: 1 }));
let mut s = *ptr.lock().unwrap();
s.something_async().await.unwrap();
println!("{:?}", s);
});
}

Downloading lots of files asynchronously

(Original code is taken from http://patshaughnessy.net/2020/1/20/downloading-100000-files-using-async-rust )
I have some code to download images concurrently using Rust, however, I'm not sure how to implement 2 things. The first, rate limiting, to avoid 429s. I tried using std::thread::sleep, however, this does not work as I would expect. (The same thing happens with tokio::time::sleep) (I did not write the original code, and am new to async rust, so I am unsure of the specifics of how and when the code runs.).
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let paths: Vec<String> = read_lines("links.txt")?;
let fetches = futures::stream::iter(
paths.into_iter().map(|path| {
async move {
let a = path.split('/').collect::<Vec<&str>>();
let file_name = a.last().unwrap();
tokio::time::sleep(tokio::time::Duration::from_secs(1));
match reqwest::get(&path).await {
Ok(resp) => {
if resp.status().as_u16() != 200 {
println!("failed to download");
println!("{}", path);
};
match resp.bytes().await {
Ok(bytes) => {
//println!("RESPONSE: {} bytes from {}", (bytes.len()), path);
write(format!("downloads/{}", file_name), bytes).unwrap();
}
Err(_) => println!("ERROR reading {}", path),
}
}
Err(_) => println!("ERROR downloading {}", path),
}
}
})
).buffer_unordered(200).collect::<Vec<()>>();
fetches.await;
Ok(())
}
Secondly, I want to have a list of a list of links which were "rate limited" (returned a 429 status), and print that once all other files are finished downloading. My attempt, and resulting compiler error message are as follows:
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut failed: Vec<String> = Vec::new();
let paths: Vec<String> = read_lines("links.txt")?;
let fetches = futures::stream::iter(
paths.into_iter().map(|path| {
async move {
let a = path.split('/').collect::<Vec<&str>>();
let file_name = a.last().unwrap();
match reqwest::get(&path).await {
Ok(resp) => {
if resp.status().as_u16() != 200 {
println!("failed to download {}", path);
failed.push(path);
return // To avoid downloading the file, which will not contain what we want.
};
match resp.bytes().await {
Ok(bytes) => {
//println!("RESPONSE: {} bytes from {}", (bytes.len()), path);
write(format!("downloads/{}", file_name), bytes).unwrap();
}
Err(_) => println!("ERROR reading {}", path),
}
}
Err(_) => println!("ERROR downloading {}", path),
}
}
})
).buffer_unordered(200).collect::<Vec<()>>();
fetches.await;
Ok(())
}
error[E0507]: cannot move out of `failed`, a captured variable in an `FnMut` closure
--> src/main.rs:28:20
|
24 | let mut failed: Vec<String> = Vec::new();
| ---------- captured outer variable
...
28 | async move {
| ____________________^
29 | | let a = path.split('/').collect::<Vec<&str>>();
30 | | let file_name = a.last().unwrap();
31 | | match reqwest::get(&path).await {
... |
35 | | failed.push(path);
| | ------
| | |
| | move occurs because `failed` has type `Vec<String>`, which does not implement the `Copy` trait
| | move occurs due to use in generator
... |
47 | | }
48 | | }
| |_________^ move out of `failed` occurs here
How can I do this?

Why do I get "expected type Future" error using match statement on Result?

I'm trying to use a function in an external crate, it is supposed to return a Result<T, E> struct as implied by the function's signature:
pub async fn market_metrics(symbols: &[String]) -> Result<Vec<market_metrics::Item>, ApiError>
I'm trying to unpack the Result<T, E> with a match statement as instructed in Rust's documentation, but I am getting this error for some reason:
use tastyworks::market_metrics;
fn main() {
let symbols = &[String::from("AAPL")];
let m = market_metrics(symbols);
match m {
Ok(v) => v,
Err(e) => panic!(e),
}
}
error[E0308]: mismatched types
--> src/main.rs:7:9
|
7 | Ok(v) => v,
| ^^^^^ expected opaque type, found enum `std::result::Result`
|
::: /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/tastyworks-0.13.0/src/lib.rs:79:52
|
79 | pub async fn market_metrics(symbols: &[String]) -> Result<Vec<market_metrics::Item>, ApiError> {
| ------------------------------------------- the `Output` of this `async fn`'s expected opaque type
|
= note: expected opaque type `impl std::future::Future`
found enum `std::result::Result<_, _>`
The dependency in Cargo.toml to use this crate is:
tastyworks = "0.13"
The function you are trying to use is an async so you need to spawn an async task for it or run it in an async context. You need tokio (or another async backend) for it:
use tastyworks::market_metrics;
use tokio;
#[tokio::main]
async fn main() {
let symbols = &[String::from("AAPL")];
let m = market_metrics(symbols).await;
match m {
Ok(v) => v,
Err(e) => panic!(e),
}
}
Check some interesting related answers

How to asynchronously explore a directory and its sub-directories?

I need to explore a directory and all its sub-directories. I can explore the directory easily with recursion in a synchronous way:
use failure::Error;
use std::fs;
use std::path::Path;
fn main() -> Result<(), Error> {
visit(Path::new("."))
}
fn visit(path: &Path) -> Result<(), Error> {
for e in fs::read_dir(path)? {
let e = e?;
let path = e.path();
if path.is_dir() {
visit(&path)?;
} else if path.is_file() {
println!("File: {:?}", path);
}
}
Ok(())
}
When I try to do the same in an asynchronous manner using tokio_fs:
use failure::Error; // 0.1.6
use futures::Future; // 0.1.29
use std::path::PathBuf;
use tokio::{fs, prelude::*}; // 0.1.22
fn visit(path: PathBuf) -> impl Future<Item = (), Error = Error> {
let task = fs::read_dir(path)
.flatten_stream()
.for_each(|entry| {
println!("{:?}", entry.path());
let path = entry.path();
if path.is_dir() {
let task = visit(entry.path());
tokio::spawn(task.map_err(drop));
}
future::ok(())
})
.map_err(Error::from);
task
}
Playground
I get the following error:
error[E0391]: cycle detected when processing `visit::{{opaque}}#0`
--> src/lib.rs:6:28
|
6 | fn visit(path: PathBuf) -> impl Future<Item = (), Error = Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires processing `visit`...
--> src/lib.rs:6:1
|
6 | fn visit(path: PathBuf) -> impl Future<Item = (), Error = Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which requires evaluating trait selection obligation `futures::future::map_err::MapErr<impl futures::future::Future, fn(failure::error::Error) {std::mem::drop::<failure::error::Error>}>: std::marker::Send`...
= note: ...which again requires processing `visit::{{opaque}}#0`, completing the cycle
note: cycle used when checking item types in top-level module
--> src/lib.rs:1:1
|
1 | / use failure::Error; // 0.1.6
2 | | use futures::Future; // 0.1.29
3 | | use std::path::PathBuf;
4 | | use tokio::{fs, prelude::*}; // 0.1.22
... |
20| | task
21| | }
| |_^
error[E0391]: cycle detected when processing `visit::{{opaque}}#0`
--> src/lib.rs:6:28
|
6 | fn visit(path: PathBuf) -> impl Future<Item = (), Error = Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: ...which requires processing `visit`...
--> src/lib.rs:6:1
|
6 | fn visit(path: PathBuf) -> impl Future<Item = (), Error = Error> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which again requires processing `visit::{{opaque}}#0`, completing the cycle
note: cycle used when checking item types in top-level module
--> src/lib.rs:1:1
|
1 | / use failure::Error; // 0.1.6
2 | | use futures::Future; // 0.1.29
3 | | use std::path::PathBuf;
4 | | use tokio::{fs, prelude::*}; // 0.1.22
... |
20| | task
21| | }
| |_^
What is the correct way of exploring a directory and its sub-directories asynchronously while propagating all the errors?
I would make several modifications to rodrigo's existing answer:
Return a Stream from the function, allowing the caller to do what they need with a given file entry.
Return an impl Stream instead of a Box<dyn Stream>. This leaves room for more flexibility in implementation. For example, a custom type could be created that uses an internal stack instead of the less-efficient recursive types.
Return io::Error from the function to allow the user to deal with any errors.
Accept a impl Into<PathBuf> to allow a nicer API.
Create an inner hidden implementation function that uses concrete types in its API.
Futures 0.3 / Tokio 0.2
In this version, I avoided the deeply recursive calls, keeping a local stack of paths to visit (to_visit).
use futures::{stream, Stream, StreamExt}; // 0.3.1
use std::{io, path::PathBuf};
use tokio::fs::{self, DirEntry}; // 0.2.4
fn visit(path: impl Into<PathBuf>) -> impl Stream<Item = io::Result<DirEntry>> + Send + 'static {
async fn one_level(path: PathBuf, to_visit: &mut Vec<PathBuf>) -> io::Result<Vec<DirEntry>> {
let mut dir = fs::read_dir(path).await?;
let mut files = Vec::new();
while let Some(child) = dir.next_entry().await? {
if child.metadata().await?.is_dir() {
to_visit.push(child.path());
} else {
files.push(child)
}
}
Ok(files)
}
stream::unfold(vec![path.into()], |mut to_visit| {
async {
let path = to_visit.pop()?;
let file_stream = match one_level(path, &mut to_visit).await {
Ok(files) => stream::iter(files).map(Ok).left_stream(),
Err(e) => stream::once(async { Err(e) }).right_stream(),
};
Some((file_stream, to_visit))
}
})
.flatten()
}
#[tokio::main]
async fn main() {
let root_path = std::env::args().nth(1).expect("One argument required");
let paths = visit(root_path);
paths
.for_each(|entry| {
async {
match entry {
Ok(entry) => println!("visiting {:?}", entry),
Err(e) => eprintln!("encountered an error: {}", e),
}
}
})
.await;
}
Futures 0.1 / Tokio 0.1
use std::path::PathBuf;
use tokio::{fs, prelude::*}; // 0.1.22
use tokio_fs::DirEntry; // 1.0.6
fn visit(
path: impl Into<PathBuf>,
) -> impl Stream<Item = DirEntry, Error = std::io::Error> + Send + 'static {
fn visit_inner(
path: PathBuf,
) -> Box<dyn Stream<Item = DirEntry, Error = std::io::Error> + Send + 'static> {
Box::new({
fs::read_dir(path)
.flatten_stream()
.map(|entry| {
let path = entry.path();
if path.is_dir() {
// Optionally include `entry` if you want to
// include directories in the resulting
// stream.
visit_inner(path)
} else {
Box::new(stream::once(Ok(entry)))
}
})
.flatten()
})
}
visit_inner(path.into())
}
fn main() {
tokio::run({
let root_path = std::env::args().nth(1).expect("One argument required");
let paths = visit(root_path);
paths
.then(|entry| {
match entry {
Ok(entry) => println!("visiting {:?}", entry),
Err(e) => eprintln!("encountered an error: {}", e),
};
Ok(())
})
.for_each(|_| Ok(()))
});
}
See also:
How do I synchronously return a value calculated in an asynchronous Future in stable Rust?
Your code has two errors:
First, a function returning impl Trait cannot currently be recursive, because the actual type returned would depend on itself.
To make your example work, you need to return a sized type. The simple candidate is a trait object, that is, a Box<dyn Future<...>>:
fn visit(path: PathBuf) -> Box<dyn Future<Item = (), Error = Error>> {
// ...
let task = visit(entry.path());
tokio::spawn(task.map_err(drop));
// ...
Box::new(task)
}
There is still your second error:
error[E0277]: `dyn futures::future::Future<Item = (), Error = failure::error::Error>` cannot be sent between threads safely
--> src/lib.rs:14:30
|
14 | tokio::spawn(task.map_err(drop));
| ^^^^^^^^^^^^^^^^^^ `dyn futures::future::Future<Item = (), Error = failure::error::Error>` cannot be sent between threads safely
|
::: /root/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-0.1.22/src/executor/mod.rs:131:52
|
131 | where F: Future<Item = (), Error = ()> + 'static + Send
| ---- required by this bound in `tokio::executor::spawn`
|
= help: the trait `std::marker::Send` is not implemented for `dyn futures::future::Future<Item = (), Error = failure::error::Error>`
= note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<dyn futures::future::Future<Item = (), Error = failure::error::Error>>`
= note: required because it appears within the type `std::boxed::Box<dyn futures::future::Future<Item = (), Error = failure::error::Error>>`
= note: required because it appears within the type `futures::future::map_err::MapErr<std::boxed::Box<dyn futures::future::Future<Item = (), Error = failure::error::Error>>, fn(failure::error::Error) {std::mem::drop::<failure::error::Error>}>`
This means that your trait object is not Send so it cannot be scheduled for execution in another thread using tokio::spawn(). Fortunately, this is easy to fix: just add + Send to your trait object:
fn visit(path: PathBuf) -> Box<dyn Future<Item = (), Error = Error> + Send> {
//...
}
See the full code in the Playground.

How do I convert an iterator into a stream on success or an empty stream on failure?

I'd like to take a regular iterator and turn it into a stream so that I can do further stream processing. The trouble is that I may have an iterator or an error to deal with. I think I'm pretty close with this:
#[macro_use]
extern crate log;
extern crate futures; // 0.1.21
extern crate tokio;
use futures::prelude::*;
use futures::{future, stream};
use std::fmt::Debug;
use std::net::{SocketAddr, ToSocketAddrs};
fn resolve(addrs: impl ToSocketAddrs + Debug) -> impl Stream<Item = SocketAddr, Error = ()> {
match addrs.to_socket_addrs() {
Ok(iter) => stream::unfold(iter, |iter| match iter.next() {
Some(a) => Some(future::ok((a, iter))),
None => None,
}),
Err(e) => {
error!("could not resolve socket addresses {:?}: {:?}", addrs, e);
stream::empty()
}
}
}
fn main() {
let task = resolve("1.2.3.4:12345")
.map_err(|e| error!("{:?}", e))
.for_each(|addr| info!("{:?}", addr))
.fold();
tokio::run(task);
}
playground
error[E0308]: match arms have incompatible types
--> src/main.rs:12:5
|
12 | / match addrs.to_socket_addrs() {
13 | | Ok(iter) => stream::unfold(iter, |iter| match iter.next() {
14 | | Some(a) => Some(future::ok((a, iter))),
15 | | None => None,
... |
20 | | }
21 | | }
| |_____^ expected struct `futures::stream::Unfold`, found struct `futures::stream::Empty`
|
= note: expected type `futures::stream::Unfold<<impl ToSocketAddrs + Debug as std::net::ToSocketAddrs>::Iter, [closure#src/main.rs:13:42: 16:10], futures::FutureResult<(std::net::SocketAddr, <impl ToSocketAddrs + Debug as std::net::ToSocketAddrs>::Iter), _>>`
found type `futures::stream::Empty<_, _>`
note: match arm with an incompatible type
--> src/main.rs:17:19
|
17 | Err(e) => {
| ___________________^
18 | | error!("could not resolve socket addresses {:?}: {:?}", addrs, e);
19 | | stream::empty()
20 | | }
| |_________^
error[E0277]: the trait bound `(): futures::Future` is not satisfied
--> src/main.rs:27:10
|
27 | .for_each(|addr| info!("{:?}", addr))
| ^^^^^^^^ the trait `futures::Future` is not implemented for `()`
|
= note: required because of the requirements on the impl of `futures::IntoFuture` for `()`
error[E0599]: no method named `fold` found for type `futures::stream::ForEach<futures::stream::MapErr<impl futures::Stream, [closure#src/main.rs:26:18: 26:39]>, [closure#src/main.rs:27:19: 27:45], ()>` in the current scope
--> src/main.rs:28:10
|
28 | .fold();
| ^^^^
|
= note: the method `fold` exists but the following trait bounds were not satisfied:
`&mut futures::stream::ForEach<futures::stream::MapErr<impl futures::Stream, [closure#src/main.rs:26:18: 26:39]>, [closure#src/main.rs:27:19: 27:45], ()> : futures::Stream`
`&mut futures::stream::ForEach<futures::stream::MapErr<impl futures::Stream, [closure#src/main.rs:26:18: 26:39]>, [closure#src/main.rs:27:19: 27:45], ()> : std::iter::Iterator`
The hint is pretty obvious. The two Results I'm returning from the match differ and should be the same. Now, how can I do that so that I return a stream?
Rust is a statically typed language which means that the return type of a function has to be a single type, known at compile time. You are attempting to return multiple types, decided at runtime.
The closest solution to your original is to always return the Unfold stream:
fn resolve(addrs: impl ToSocketAddrs) -> impl Stream<Item = SocketAddr, Error = ()> {
stream::unfold(addrs.to_socket_addrs(), |r| {
match r {
Ok(mut iter) => iter.next().map(|addr| future::ok((addr, Ok(iter)))),
Err(_) => None,
}
})
}
But why reinvent the wheel?
futures::stream::iter_ok
Converts an Iterator into a Stream which is always ready to yield the next value.
Subsequent versions of the futures crate implement Stream for Either, which makes this very elegant:
fn resolve(addrs: impl ToSocketAddrs) -> impl Stream<Item = SocketAddr, Error = ()> {
match addrs.to_socket_addrs() {
Ok(iter) => stream::iter_ok(iter).left_stream(),
Err(_) => stream::empty().right_stream(),
}
}
It's straightforward to backport this functionality to futures 0.1 (maybe someone should submit it as a PR for those who are stuck on 0.1...):
enum MyEither<L, R> {
Left(L),
Right(R),
}
impl<L, R> Stream for MyEither<L, R>
where
L: Stream,
R: Stream<Item = L::Item, Error = L::Error>,
{
type Item = L::Item;
type Error = L::Error;
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
match self {
MyEither::Left(l) => l.poll(),
MyEither::Right(r) => r.poll(),
}
}
}
trait EitherStreamExt {
fn left_stream<R>(self) -> MyEither<Self, R>
where
Self: Sized;
fn right_stream<L>(self) -> MyEither<L, Self>
where
Self: Sized;
}
impl<S: Stream> EitherStreamExt for S {
fn left_stream<R>(self) -> MyEither<Self, R> {
MyEither::Left(self)
}
fn right_stream<L>(self) -> MyEither<L, Self> {
MyEither::Right(self)
}
}
Even better, use the fact that Result is an iterator and Stream::flatten exists:
fn resolve(addrs: impl ToSocketAddrs) -> impl Stream<Item = SocketAddr, Error = ()> {
stream::iter_ok(addrs.to_socket_addrs())
.map(stream::iter_ok)
.flatten()
}
Or if you really want to print errors:
fn resolve(addrs: impl ToSocketAddrs) -> impl Stream<Item = SocketAddr, Error = ()> {
stream::once(addrs.to_socket_addrs())
.map(stream::iter_ok)
.map_err(|e| eprintln!("err: {}", e))
.flatten()
}
See also:
Conditionally return empty iterator from flat_map
Conditionally iterate over one of several possible iterators
What is the correct way to return an Iterator (or any other trait)?

Resources