Нет. Эта реализация не является безопасной. По двум причинам.
1) Ссылка на TcpStream
может жить дольше, чем сама структура
let bad_ref = {
let buffer = BufTcpStream::new(...);
let (i, o) = buffer.reach();
*i.get_ref()
};// <-- buffer is dropped here
// bad_ref is now an invalid ref but the compiler won't see it
bad_ref.write(b"hello world").unwrap();
*i.get_ref()
имеет тип &'a TcpStream
, но &'a
время жизни никогда не определяется компилятором, поэтому оно не учитывает это.
Это можно исправить в методе reach()
, связав время жизни self
с возвращенными ссылками. :
fn reach<'b>(&'b mut self) -> (
&mut BufReader<&'b TcpStream>,
&mut BufWriter<&'b TcpStream>
) {
(&mut self.input, &mut self.output)
}
2) TcpStream
отбрасывается до BufReader
и BufWriter
pub struct BufTcpStream<'a> {
_socket: Pin<Box<TcpStream>>,// First to be dropped |
input : BufReader<&'a TcpStream>,// Second to be dropped |
output: BufWriter<&'a TcpStream>,// Last to be dropped V
}
Это можно исправить, поместив _socket
в качестве последнего элемента:
pub struct BufTcpStream<'a> {
input : BufReader<&'a TcpStream>,// First to be dropped |
output: BufWriter<&'a TcpStream>,// Second to be dropped |
_socket: Pin<Box<TcpStream>>,// Last to be dropped V
}
На самом деле время жизни 'a
в struct BufTcpStream<'a>
бесполезно. Время жизни небезопасных ссылок может быть установлено на 'static
.
Вот фиксированная реализация:
use std::io::{BufReader, BufWriter};
use std::net::TcpStream;
use std::pin::Pin;
pub struct BufTcpStream {
input : BufReader<&'static TcpStream>,
output: BufWriter<&'static TcpStream>,
_socket: Pin<Box<TcpStream>>,
}
impl BufTcpStream {
pub fn new(socket: TcpStream) -> Self {
let pin = Box::pin(socket);
unsafe {
Self{
input : BufReader::new(std::mem::transmute(&*pin)),
output: BufWriter::new(std::mem::transmute(&*pin)),
_socket: pin,
}
}
}
pub fn reach<'b>(&'b mut self) -> (
&mut BufReader<&'b TcpStream>,
&mut BufWriter<&'b TcpStream>
) {
unsafe {
std::mem::transmute((&mut self.input, &mut self.output))
}
}
}