Your code doesn't compile because you are using a reference that the compiler attributed as being good only while the function is running -- even if the Arc
holding it may live much longer.
But what would happen in the following scenario:
- a
User
holds the last Arc to TcpStream
User
is dropped, but the stream
field is dropped first
?
You should be careful when dropping -- re-arranjing the declaration order deals with that: fields are dropped in the order they were declared.
That being said, there is indeed a recommended way to achieve self references -- it is documented in the Pin
module:
https://doc.rust-lang.org/std/pin/index.html#example-self-referential-struct
But you don't need it here, since you are not using a reference to your own struct -- and the "pinning" is played by Arc in your code.
Even so, if you want to go through the unsafe
route, you'll need to tell the compiler to turn off some of its checks, as you "know what you are doing".
There is little to no benefit in going unsafe here (see John Kugelman's answer for a safe version), but, anyway, here it is, since it avoids creating & destroying 2 Arcs per connection, which might save you some dozens or hunderds of nano-seconds.
Without further ado, here is a version -- closer to your original code -- that compiles:
(but, please, notice the compiler is no longer proving this code is correct)
use std::io::{BufReader, BufWriter};
use std::net::TcpStream;
use std::sync::Arc;
struct User {
reader: BufReader<&'static TcpStream>,
writer: BufWriter<&'static TcpStream>,
// notice the field order is important here:
// `stream` must be dropped after `reader` and `writer` or else they
// would hold invalid references. Dropping is done in the same order as declaration.
stream: Arc<TcpStream>,
}
fn accept_socket(users: &mut Vec<User>, stream: Arc<TcpStream>) {
let stream_ref = unsafe { &*Arc::as_ptr(&stream) };
let user = User {
reader: BufReader::new(stream_ref),
writer: BufWriter::new(stream_ref),
stream,
};
users.push(user);
}
fn main() {
let stream = Arc::new(TcpStream::connect("google.com:80").unwrap());
let mut users = Vec::with_capacity(16); // giving a big enough capacity may squeeze a little bit more performance -- avoids reallocations
accept_socket(&mut users, stream);
}