Error while compiling async client: cannot move out of captured outer variable in an `FnMut` closure


#1

Hello All,
I’m trying to write an async client which does the following:
* Send some request to server
* Wait for server response asynchronously.
* When server responds send some other request again.

The sample code used for testing:

	impl UnixDatagramCodec for StringDatagramCodec {
	    type In = String;
	    type Out = String;

	    fn decode(&mut self, _src: &std::os::unix::net::SocketAddr, buf: &[u8]) -> Result<Self::In, io::Error>
	    {
		let decoded = String::from_utf8_lossy(buf).into_owned();
		Ok(decoded)
	    }

	    fn encode(&mut self, msg: Self::Out, buf: &mut Vec<u8>) -> Result<PathBuf, io::Error>
	    {
		buf.extend_from_slice(&msg.into_bytes());
		Ok(Path::new(SERVER_PATH).to_path_buf())
	    }
	}


	fn main() {
	    let mut core = Core::new().unwrap();
	    let handle = core.handle();

		let mut sock = std::os::unix::net::UnixDatagram::bind(CLIENT_PATH).unwrap();
		let sock_copy = sock.try_clone().expect("try clone failed");

	    let nSock = tokio_uds::UnixDatagram::from_datagram(sock, &handle).unwrap().framed(StringDatagramCodec);
	    let (sink, stream) = nSock.split();

		let f = sink.send("ATTACH".to_string()).into_future().then(|_| Ok(()));
		handle.spawn(f);

		let data = stream.map(|msg| {
			println!("Response from server: {}", msg);
			// send next request to server
			let newSock = tokio_uds::UnixDatagram::from_datagram(sock_copy, &handle).unwrap().framed(StringDatagramCodec);
			let (sink1, stream1) = newSock.split();
			let f1 = sink1.send("SCAN".to_string()).into_future().then(|_| Ok(()));
			handle.spawn(f1);
		}).collect().into_future();
		core.run(data);
	}

This throws the following error.

Any help on this would be appreciated!!!
Thanks in advance!!!


#2

The common solution looks to be adding;

let sock_copy = sock_copy.try_clone().expect("try clone failed");
let newSock = ...

This is because |msg| closure is called multiple times but from_datagram is trying to move the datagram which can only happen once.

A fancy solution is making the copy into a closure that you call when needed;

let sock_copy = || sock_copy.try_clone().expect("try clone failed");
... from_datagram(sock_copy(), ...

#3

I think there should be a better way to structure this such that you don’t need to try_clone anything, nor create a new socket each time. Something like the following (untested, but hopefully close enough):

let sock = std::os::unix::net::UnixDatagram::bind(CLIENT_PATH).unwrap();
let nSock = tokio_uds::UnixDatagram::from_datagram(sock, &handle).unwrap().framed(StringDatagramCodec);
// Send "ATTACH" first, then chain a continuation ...
let f = nsock.send("ATTACH".to_string()).and_then(|sock| {
     // Now split into the sink and stream, and wire them up ...
     let (sink, stream) = sock.split();
     // Map each response from peer to a new request
     let m = stream.map(|msg| {
              println!("Response from server: {}", msg);
              "SCAN".to_string()
     });
     // Send the requests to the peer, and return that as a future back to the reactor
     sink.send_all(m)
});
core.run(f);

#4

Thanks a lot. This seems to be solving the problem!!!