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.
&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.
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.
// 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();
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.