I am writing a network client and I have a struct which represents a "logical connection", i.e. connection to server which can be recovered, produce instrumentation events and so on. One of members is tokio::net::TcpStream
which is "physical" connection.
pub struct Connected {
addr: SocketAddr,
...
tcp: TcpStream,
}
When I send a request and_then
decode response, I use tokio::io::write_all
. In typical async fashion, it moves 2 parameters (TcpStream and buffer) and will return them in future.
pub fn request<R>(mut self, request: R) -> impl Future<Item=(Self,u32,R::Response), Error=String>
where R: protocol::Request
{
write_all(self.tcp, buff).
...
}).and_then(|(tcp, mut buff)| {
let len = BigEndian::read_u32(&buff);
println!("Response len: {}", len);
buff.resize(len as usize, 0_u8);
read_exact(tcp, buff)
}).map(|(tcp, buff)| {
let mut cursor = Cursor::new(buff);
let (correlation_id, response) = read_response::<R::Response>(&mut cursor);
println!("CorrId: {}, Response: {:#?}", correlation_id, response);
(tcp, corr_id, response)
})
Now my problem. In write_all(self.tcp, )
self
will be moved but in future only self.tcp
will emerge, thus, self
is lost to me and I kinda need it.
I've tried to implement AsyncWrite for Connected so that I could pass my "logical" connection through async pipeline and receive it back in the future, but it feels wrong in many ways (boilerpalte code, new functions introduced in tokio in future won't be overriden, etc).
I am thinking about "detaching" tcp member from Connected struct and re-attaching it when future is complete because I have vague memories I've seen it somewhere.
Another solution I can think of is to improve tokio-io itself by modifying to accept to_async_write
, then I can write one-liner adapter for my Connected struct to return tcp
member.
But thinking about it more, what if I want to produce periodic statistics on my "logical" connections and they are "moved" into async at the moment. It means I just can not get access of them. So "detach-attach" solution make more sense now.
This can not be unique problem. What is the best practice to associate application-specific data with tcp stream yet allow stream to move in and out of async future?