У меня есть код, который нужно прочитать из нескольких TcpStream. Один из этих TcpStream всегда будет там, а другой может быть, а может и не быть.
Я написал код раньше и (наивно) использовал условия в макросе tokio select. К сожалению, я быстро понял, что макрос сначала оценит руку (чтобы получить будущее) и только после этого проверит условие и на основе этого условия либо запустит будущее, либо пропустит его.
В результате, когда я запускаю этот код, я получаю:
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
}
}
}
Вопрос, который у меня есть, как справиться с этим?
Я наткнулся на следующие идеи, и я не доволен ни одной из них
Добавьте оператор if. Если stream2.is_some(), то выберите двумя руками, в противном случае выберите с одной меткой. Это работает, но приводит к довольно уродливому дублированию кода.
Я посмотрел в направлении FutureUnordered. Однако, принимая во внимание, что приведенный выше код будет выполняться в цикле, я отказался от этого подхода, потому что мне не понравилось, что я пытаюсь заимствовать вещи как изменяемые в цикле.
Я думал использовать «поддельный» TcpStream, просто чтобы удалить часть Option<>. Однако это тоже выглядит некрасиво.
Я ищу способ, который позволит мне сохранить этот выбор! над опцией
Это немного странно для меня, и мне любопытно, почему они сделали это изменение. Поведение старой версии имеет для меня гораздо больше смысла.
@cdhowie Я никогда не использовал старую версию. Однако я согласен, что старое поведение имело больше смысла.
Вы можете перенести обработку потока optiona в другое будущее, возможно, возвращая пользовательскую ошибку. Затем сопоставление шаблона при успешном чтении:
use tokio::io::AsyncReadExt;
use tokio::net::TcpStream;
use std::io::{Error, ErrorKind};
#[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];
let read2 = async move {
match stream2 {
Some(mut stream) => stream.read(&mut buf2).await,
_ => Err(Error::new(ErrorKind::Other, "No available stream")),
}
};
tokio::select! {
result = stream1.read(&mut buf) => {
// do something here
}
Ok(result) = read2 => {
// do somethihng here
}
}
}
Если вам не нравится пользовательская ошибка, вы можете перевернуть все в Option<Result>
и сопоставить все с шаблоном:
...
let read2 = async move {
match stream2 {
Some(mut stream) => Some(stream.read(&mut buf2).await),
None => None,
}
};
tokio::select! {
result = stream1.read(&mut buf) => {
// do something here
}
Some(result) = read2 => {
// do somethihng here
}
}
...
Спасибо @Netwave. Это именно то, что я искал. Я пошел на прогулку, и у меня возникла похожая идея, и было удивительно вернуться и увидеть, что у вас есть полное решение.
Похоже, это поведение изменилось. Старая версия документов: «Кроме того, каждая ветвь может включать необязательное предварительное условие
if
. Это предварительное условие оценивается как до<async expression>
. Если предварительное условие возвращаетfalse
, ветвь полностью отключена». Но текущая версия: «Кроме того, каждая ветвь может включать необязательное предварительное условиеif
. Если предварительное условие возвращаетfalse
, то ветвь отключается. Предоставленное<async expression>
все еще оценивается, но результирующее будущее никогда не опрашивается».