I have a client handling a Future
that does some stuff. Is it possible to use impl Future<Item = (), Error = io::Error>
as a return type and make better error handling?
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
}
I cannot keep the io::Error
type through all nested closures/futures. The compiler throws the 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`
I made chaining map/and_then error handling, but problem is I don't know how to get TcpStream
inside final .then
closure. The only place I found TcpStream
is inside WriteAll struct, however it's private. Besides, write_all consumes stream
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);
}
The question mark operator It is a unary postfix operator that can only be applied to the types Result<T, E> and Option<T> . When applied to values of the Result<T, E> type, it propagates errors. If the value is Err(e) , then it will return Err(From::from(e)) from the enclosing function or closure.
The ? in Rust is the questionmark operator. It's a piece of syntax that replaces a very common case of error handling. The following piece of code deals with 2 operations that can fail. It tries to open a file and read the username inside.
TL;DR: you don't use the ?
operator.
Since you didn't provide one, here is a MCVE of your problem. Note that we have no idea what the error type is of your serialize_addr
function, so I had to pick something:
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`
As the error message states:
the
?
operator can only be used in a function that returnsResult
orOption
(or another type that implementsstd::ops::Try
)
and
cannot use the
?
operator in a function that returnsThen<WriteAll<TcpStream, Vec<u8>>, Result<(), io::Error>, [closure]>
Instead, leverage the fact that Result
can be treated as a future and let it participate in the chain of functions.
Additionally, just like everywhere else in Rust, you need to have a unified error type. I've chosen Box<dyn Error>
for simplicity. This can be achieved using map_err
and Into::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(()))
}
In the future, async
/ await
syntax will make this easier to follow.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With