【问题标题】:How can I use the question mark operator to handle errors in Tokio futures?如何使用问号运算符来处理 Tokio 期货中的错误?
【发布时间】:2019-02-28 08:24:57
【问题描述】:

我有一个客户处理 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
}

我无法在所有嵌套的闭包/期货中保留 io::Error 类型。编译器抛出错误

error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
   --> src/client.rs:134:29
    |
134 |         let addr: Vec<u8> = serialize_addr(stream.local_addr()?, magic)?;
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `futures::future::then::Then<tokio_io::io::write_all::WriteAll<tokio_tcp::stream::TcpStream, std::vec::Vec<u8>>, std::result::Result<(), std::io::Error>, [closure@src/client.rs:135:38: 138:10]>`
    |
    = help: the trait `std::ops::Try` is not implemented for `futures::future::then::Then<tokio_io::io::write_all::WriteAll<tokio_tcp::stream::TcpStream, std::vec::Vec<u8>>, std::result::Result<(), std::io::Error>, [closure@src/client.rs:135:38: 138:10]>`
    = note: required by `std::ops::Try::from_error`

我进行了链接 map/and_then 错误处理,但问题是我不知道如何在 final .then 闭包中获取 TcpStream。我发现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);
}

【问题讨论】:

  • 例如,您的代码使用了我们不知道其签名的未定义类型和方法。您的错误消息甚至与代码不对应。
  • 下面的代码使用and_then,为什么还要使用map(... write_all)?您不能随意更改您调用的方法并期望它起作用。使用and_then时,未来的成功值为(TcpStream, Vec&lt;u8&gt;)
  • 我不知道为什么,但是如果没有转换为 serialize_addr(remote_addr).map_err(|_| io::Error::from(io::ErrorKind::AddrNotAvailable)),我就不能使用 .and_then 而不是 .map

标签: error-handling rust future rust-tokio


【解决方案1】:

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(()))
    })
}
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
  --> src/lib.rs:11:20
   |
11 |         let addr = serialize_addr()?;
   |                    ^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `futures::future::then::Then<tokio_io::io::write_all::WriteAll<tokio_tcp::stream::TcpStream, std::vec::Vec<u8>>, std::result::Result<(), std::io::Error>, [closure@src/lib.rs:12:38: 14:10]>`
   |
   = help: the trait `std::ops::Try` is not implemented for `futures::future::then::Then<tokio_io::io::write_all::WriteAll<tokio_tcp::stream::TcpStream, std::vec::Vec<u8>>, std::result::Result<(), std::io::Error>, [closure@src/lib.rs:12:38: 14:10]>`
   = note: required by `std::ops::Try::from_error`

如错误消息所述:

? 运算符只能用于返回ResultOption(或实现std::ops::Try 的其他类型)的函数中

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

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

此外,就像 Rust 中的其他任何地方一样,您需要有一个统一的错误类型。为简单起见,我选择了Box&lt;dyn Error&gt;。这可以使用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(()))
}

在未来,async / await 语法将使这更容易理解。

【讨论】:

    【解决方案2】:

    两个流的解决方案:

    fn handle_client(addr: &SocketAddr) -> impl Future<Item = (), Error = Error> {
        TcpStream::connect(addr)
            .map_err(Into::into)
            .and_then(|remote_stream| {
                remote_stream
                    .local_addr()
                    .map(|remote_addr| (remote_stream, remote_addr))
            })
            .map_err(Into::into)
            .and_then(|(remote_stream, remote_addr)| {
                TcpStream::connect(&"".parse().unwrap())
                    .map(move |proxy_stream| (remote_stream, proxy_stream, remote_addr))
            })
            .and_then(|(remote_stream, proxy_stream, remote_addr)| {
                serialize_addr(remote_addr)
                    .map(|info| (remote_stream, proxy_stream, info))
                    .map_err(|_| io::Error::from(io::ErrorKind::AddrNotAvailable))
            })
            .and_then(|(remote_stream, proxy_stream, info)| {
                write_all(proxy_stream, info).map(|proxy_stream| (remote_stream, proxy_stream.0))
            })
            .and_then(|(remote_stream, proxy_stream)| {
                // working with streams
            })
            .then(|_result| Ok(()))
    }
    

    【讨论】:

      猜你喜欢
      • 2020-07-15
      • 1970-01-01
      • 2016-12-31
      • 2017-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多