Proper way to work with TcpStream?

I'm learning Rust having mostly Java background.

fn main() -> std::io::Result<()> {
    let mut stream = TcpStream::connect("127.0.0.1:34254")?;

    stream.write(&[1])?;
    stream.read(&mut [0; 128])?;
    Ok(())
}

It's fairly easy to create two threads to read and write in Java. However Rust is stubborn here complaining that reading and writing threads will outlive the creator function. It's obvious they will, but how do I calm down Rust telling that it's the expected behavior? Another problem I didn't meet yet but expect that Rust will tell to use something like Mutex<TcpStream> in threads, but I do not want an exclusive access to TcpStream, because read and write operations need to be executed concurrently. I would prefer to do not use any third party crates for a solution.

You can use try_clone() on a tcpstream.

&TcpStream: Read + Write (because TcpStream is just a file descriptor). This means you can freely do I/O on just a &TcpStream, so moving a clone of an Arc<TcpStream> into each thread would work.

TcpStream::try_clone is also an option. AFAICT it's only fallible if you hit the file descriptor limit.

You absolutely can't have references to a TcpStream that got freed, so the threads can't hold &s to the TcpStream on the stack. The equivalent of Java's "reference" is Arc.

1 Like

Thanks for the suggestion. It has resolved the first part of my question, however,

use std::sync::{Mutex,Arc};
use std::io::Write;
use std::thread;

use std::io::prelude::*;
use std::net::TcpStream;

fn main() -> std::io::Result<()> {
    let stream = TcpStream::connect("127.0.0.1:34254")?;
    let stream = Arc::new(stream);
    let reader_stream = Arc::clone(&stream);
    let writer_stream = Arc::clone(&stream);
    thread::spawn(move || {
        reader_stream.read(&mut [0; 128]).unwrap();
    });
    writer_stream.write(&[1])?;
    
    Ok(())
} 

However, as I anticipated,

error[E0596]: cannot borrow data in an `Arc` as mutable
  --> main.rs:14:9
   |
14 |      

   reader_stream.read(&mut [0; 128]).unwrap();
   |         ^^^^^^^^^^^^^ cannot borrow as mutable

   |

   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<TcpStream>`


error[E0596]: cannot borrow data in an `Arc` as mutable
  --> main.rs:16:5
   |
16 |     w

riter_stream.write(&[1])?;
   |     ^^^^^^^^^^^^^ cannot borrow as mutable

   |

   = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<TcpStream>`


error: aborting due to 2 previous errors; 1 warning emitted

But I can't add a Mutex because my read or write operation will block other from a processing.

try_clone creates an independently owned stream referencing the same underlying handle. With that you can have owned TcpStream values in each thread and you don't need Arc or Mutex. You can moved the cloned value to another thread.

1 Like

as already pointed out, &TcpStream implements both Read and Write, so you only need a shared reference to the TcpStream.

unfortuntely, auto deref coercion of Arc doesn't help in this case, you have to be more explicit:

-        reader_stream.read(&mut [0; 128]).unwrap();
+        (&*reader_stream).read(&mut [0; 128]).unwrap();

same for writer_stream.

alternatively, you can create a local binding:

// type inference works as expected
let mut reader_stream = &*reader_stream;
// alternatively, use explicit type annotation,
// can also omit the `*` operator thanks to auto deref coercion
let mut reader_stream: &TcpStream = &reader_stream;
// then can call `Read::read()` as usual
reader_stream.read(&mut [0; 128]).unwrap();
2 Likes

It's easy to overlook the & in &TcpStream in that statement, which is the significant thing.

1 Like

Awesome, let me incorporate

It's absolutely fabulous solution. What does I like especially? It's one liner fix, any fix should be like that. If it requires more lines, then it isn't a fix, it's a work around.

But

is even better, because it's one liner too, but better reflects the purpose of the my code. Thank you guys, you are awesome.

1 Like