如何使用问号运算符来处理 Tokio 期货中的错误?

abr*_*tov 5 error-handling future rust rust-tokio

我有一个客户处理一个Future做一些事情的。是否可以impl Future<Item = (), Error = io::Error>用作返回类型并进行更好的错误处理?

pub fn handle_client(client: Client) -> impl Future<Item = (), Error = io::Error> {
    let magic = client.header.magic;
    let stream_client = TcpStream::connect(&client.addr).and_then(|stream| {
        let addr: Vec<u8> = serialize_addr(stream.local_addr()?, magic)?;
        write_all(stream, addr).then(|result| {
            // some code
            Ok(())
        })
    });
    stream_client
}
Run Code Online (Sandbox Code Playgroud)

我无法io::Error通过所有嵌套的闭包/期货保持该类型。编译器抛出错误

pub fn handle_client(client: Client) -> impl Future<Item = (), Error = io::Error> {
    let magic = client.header.magic;
    let stream_client = TcpStream::connect(&client.addr).and_then(|stream| {
        let addr: Vec<u8> = serialize_addr(stream.local_addr()?, magic)?;
        write_all(stream, addr).then(|result| {
            // some code
            Ok(())
        })
    });
    stream_client
}
Run Code Online (Sandbox Code Playgroud)

我做了链接映射/and_then 错误处理,但问题是我不知道如何TcpStream进入最终.then关闭。我发现的唯一地方TcpStream是在 WriteAll 结构内,但它是私有的。此外, write_all 消耗流

use futures::Future;
use std::{io, net::SocketAddr};
use tokio::{
    io::{write_all, AsyncRead, AsyncWrite},
    net::TcpStream,
};

type Error = Box<dyn std::error::Error>;

fn serialize_addr(addr: SocketAddr) -> Result<Vec<u8>, Error> {
    Ok(vec![])
}

fn handle_client(addr: &SocketAddr) -> impl Future<Item = (), Error = Error> {
    TcpStream::connect(addr)
        .map_err(Into::into)
        .and_then(|stream| stream.local_addr().map(|stream_addr| (stream, stream_addr)))
        .map_err(Into::into)
        .and_then(|(stream, stream_addr)| serialize_addr(stream_addr).map(|info| (stream, info)))
        .map(|(stream, info)| write_all(stream, info))
        .then(|result| {
            let result = result.unwrap();
            let stream = match result.state {
                Writing { a } => a,
                _ => panic!("cannot get stream"),
            };
            // some code
            Ok(())
        })
}

fn main() {
    let addr = "127.0.0.1:8900".parse().unwrap();
    handle_client(&addr);
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 5

TL; DR:您不使用?运算符。


由于您没有提供一个,这是您的问题的MCVE。请注意,我们不知道您的serialize_addr函数的错误类型是什么,所以我不得不选择一些东西:

use futures::Future;
use std::{io, net::SocketAddr};
use tokio::{io::write_all, net::TcpStream};

fn serialize_addr() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    Ok(vec![])
}

pub fn handle_client(addr: &SocketAddr) -> impl Future<Item = (), Error = io::Error> {
    TcpStream::connect(addr).and_then(|stream| {
        let addr = serialize_addr()?;
        write_all(stream, addr).then(|_result| Ok(()))
    })
}
Run Code Online (Sandbox Code Playgroud)
use futures::Future;
use std::{io, net::SocketAddr};
use tokio::{io::write_all, net::TcpStream};

fn serialize_addr() -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    Ok(vec![])
}

pub fn handle_client(addr: &SocketAddr) -> impl Future<Item = (), Error = io::Error> {
    TcpStream::connect(addr).and_then(|stream| {
        let addr = serialize_addr()?;
        write_all(stream, addr).then(|_result| Ok(()))
    })
}
Run Code Online (Sandbox Code Playgroud)

正如错误消息所述:

?操作者只能在函数中使用该回报ResultOption(或另一类型的器具std::ops::Try

不能?在返回的函数中使用运算符Then<WriteAll<TcpStream, Vec<u8>>, Result<(), io::Error>, [closure]>

相反,利用Result可以被视为未来的事实,让它参与到功能链中。

此外,就像 Rust 中的其他地方一样,您需要有一个统一的错误类型。我选择Box<dyn Error>了简单。这可以通过使用map_errInto::into

use futures::Future;
use std::net::SocketAddr;
use tokio::{io::write_all, net::TcpStream};

type Error = Box<dyn std::error::Error>;

fn serialize_addr() -> Result<Vec<u8>, Error> {
    Ok(vec![])
}

pub fn handle_client(addr: &SocketAddr) -> impl Future<Item = (), Error = Error> {
    TcpStream::connect(addr)
        .map_err(Into::into)
        .and_then(|stream| serialize_addr().map(|addr| (stream, addr)))
        .and_then(|(stream, addr)| write_all(stream, addr).map_err(Into::into))
        .then(|_result| Ok(()))
}
Run Code Online (Sandbox Code Playgroud)

将来,async/await语法将使这更容易遵循。