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

时间:2019-02-28 08:24:58

标签: error-handling rust future 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
}

我无法在所有嵌套的闭包/将来都保留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链接错误处理,但是问题是我不知道如何在最终的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);
}

2 个答案:

答案 0 :(得分:2)

TL; DR:您无需使用 class FieldSection extends Component{ constructor(){ super() this.state ={ allFields: [] } } addField = () => { this.setState({ allFields: getFieldId()}); } removeField = (id) => { //get new array if fields without element you want to delete const editedArray = this.state.allFields.filter(num => num !== id) this.setState({allFields: editedArray}) } render () { var fields = []; for(var i=0; i<this.state.allFields; i++){ fields.push(<Field key={allFields[i]} id={allFields[i]} deleteAction = (id)=> this.removeField(id)/>) } return ( <div> <label><u>Fields</u></label> {fields} <div id="fieldButtons"> <button id="addField" type="button" onClick={addField}>Add Field </button> <button id="removeField" type="button"> Remove Field </button> </div> </div> ) } } //********Field Component************// class FieldSection extends Component{ render() { return ( <div> <div id={this.props.id}>My field</div> <div onClick={()=> this.props.deleteAcion(this.props.id}>Delete field</div> </div> ) } } 运算符。


由于您没有提供,因此这里是您问题的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` 运算符只能用于返回?Result(或实现Option的其他类型)的函数中

  

不能在返回std::ops::Try的函数中使用?运算符

相反,请充分利用Then<WriteAll<TcpStream, Vec<u8>>, Result<(), io::Error>, [closure]>可被视为未来的事实,并使其参与功能链。

此外,就像Rust中的其他地方一样,您需要具有统一的错误类型。为了简单起见,我选择了Result。这可以使用map_errInto::into

来实现
Box<dyn Error>

将来,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语法将使其更易于理解。

答案 1 :(得分:0)

两个流的解决方案:

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(()))
}