Макроусловия выбора Токио и оценка руки

У меня есть код, который нужно прочитать из нескольких 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<>. Однако это тоже выглядит некрасиво.

Я ищу способ, который позволит мне сохранить этот выбор! над опцией

Похоже, это поведение изменилось. Старая версия документов: «Кроме того, каждая ветвь может включать необязательное предварительное условие if. Это предварительное условие оценивается как до<async expression>. Если предварительное условие возвращает false, ветвь полностью отключена». Но текущая версия: «Кроме того, каждая ветвь может включать необязательное предварительное условие if. Если предварительное условие возвращает false, то ветвь отключается. Предоставленное <async expression> все еще оценивается, но результирующее будущее никогда не опрашивается».

cdhowie 17.05.2022 00:52

Это немного странно для меня, и мне любопытно, почему они сделали это изменение. Поведение старой версии имеет для меня гораздо больше смысла.

cdhowie 17.05.2022 00:53

@cdhowie Я никогда не использовал старую версию. Однако я согласен, что старое поведение имело больше смысла.

Victor Ronin 17.05.2022 03:37
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
1
3
35
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете перенести обработку потока 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. Это именно то, что я искал. Я пошел на прогулку, и у меня возникла похожая идея, и было удивительно вернуться и увидеть, что у вас есть полное решение.

Victor Ronin 16.05.2022 23:39

Другие вопросы по теме