0

I am trying to transmit packets/bytes from a TcpStream to another and vice-versa. I've created a struct Connection:

pub struct Connection {
    pub client_socket: TcpStream,
    pub server_socket: TcpStream
}

I'm trying to open two threads so I can transmit all bytes in both directions. Sadly I'm trying to use a moved value because I called connection a second time. How can I fix that ?

thread::spawn(move || loop { connection.transfer_server_client_packet(); });
thread::spawn(move || loop { connection.transfer_client_server_packet(); });
impl Connection {
    fn get_packet(&mut self, from_client: bool) -> Result<Packet, io::Error> {
        unimplemented!()
    }
    pub fn get_client_packet(&mut self) -> Result<Packet, io::Error> {
        self.get_packet(true)
    }
    pub fn get_server_packet(&mut self) -> Result<Packet, io::Error> {
        self.get_packet(false)
    }
    fn send_packet(&mut self, from_client: bool, mut packet: Packet) -> Result<Packet, io::Error> {
        unimplemented!()
    }
    pub fn send_client_packet(&mut self, packet: Packet) -> Result<Packet, io::Error> {
        self.send_packet(true, packet)
    }
    pub fn send_server_packet(&mut self, packet: Packet) -> Result<Packet, io::Error> {
        self.send_packet(false, packet)
    }
    fn transfer_packet(&mut self, from_client: bool, to_client: bool) -> Result<usize, io::Error> {
        let packet = if from_client {
            self.get_client_packet()?
        } else {
            self.get_server_packet()?
        };
        if to_client {
            self.send_client_packet(packet)?
        } else {
            self.send_server_packet(packet)?
        };
        Ok(0)
    }
    pub fn transfer_client_server_packet(&mut self) -> Result<usize, io::Error> {
        self.transfer_packet(true, false)
    }
    pub fn transfer_server_client_packet(&mut self) -> Result<usize, io::Error> {
        self.transfer_packet(false, true)
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Could you add the `transfer_server_client_packet` and `transfer_client_server_packet` signatures? It's already pretty clear what's going on (in short, they shouldn't take `self` if they don't have to), but this should simplify things both for us and for future readers. – Cerberus Nov 18 '19 at 03:40
  • Wrap your connection into an `Arc<>`. This will solve the `moved value` problem. Yet your method signatures will probably contain `&mut self` which will cause a new problem because `Arc` alone will not be allowed to be borrowed mutably. You should then consider a solution with internal mutability. First add the signatures as @Cerberus mentioned. – CoronA Nov 18 '19 at 05:36

1 Answers1

2

Once you move a value — in this case, the Connection — you can't use it again. If you need two threads to have mutable access to the same connection then you can either clone it or share it using synchronisation primitives, such as Arc.

For example:

let connection = Arc::new(Mutex::new(Connection::new()));
let connection_server_client = Arc::clone(&connection);
let connection_client_server = Arc::clone(&connection);

thread::spawn(move || loop {
    let connection = connection_server_client.lock().unwrap();
    connection.transfer_server_client_packet();
});

thread::spawn(move || loop {
    let connection = connection_client_server.lock().unwrap();
    connection.transfer_client_server_packet();
});

This setup manages the shared resource so that only one thread can mutate it at a time. It also will make sure that the resource is dropped when both threads have finished with it.


Having said that, I would consider a refactor of your code: the two threads do not actually need the same TcpStreams. The only reason that sharing becomes necessary is that you have stored both streams in the same struct, meaning that you can't borrow one without also borrowing the other.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204