I have a code that needs to be read from several TcpStream. One of these TcpStream will always be there, but another may or may not.
I wrote the code before and (naively) used conditions on tokio select macro. Unfortunately, I quickly learned that the macro will first evaluate the arm (to get the future) and only after that will check a condition and based on this condition will either start the future or skip it.
As a result, as I run this code I will get:
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:16:35
use tokio::io::AsyncReadExt;
use tokio::net::TcpStream;
#[tokio::main]
async fn main() {
let mut stream1 : TcpStream = TcpStream::connect("info.cern.ch:80").await.unwrap();
let mut stream2 : Option<TcpStream> = None;
let mut buf = vec![0_u8; 1024];
let mut buf2 = vec![0_u8; 1024];
tokio::select! {
result = stream1.read(&mut buf) => {
// do something here
}
result = stream2.as_mut().unwrap().read(&mut buf2), if stream2.is_some() => {
// do somethihng here
}
}
}
The question which I have is how to handle this?
I stumbled on following ideas and I am not happy about any of these
Add if statement. If stream2.is_some() then have select with two arms, otherwise select with one mark. This works but results in quite ugly code duplication
I looked into FutureUnordered direction. However, taking into account that the above code will be executed in the loop, I bailed out from this approach, because it didn't like that I try to borrow things as mutable in the loop.
I was thinking to have a "fake" TcpStream, just to remove Option<> part. However, it also looks ugly.
I am looking for some way that will allow me to keep this select! over Option