如何使用BufReader和BufWriter在数据结构中存储TcpStream

时间:2019-10-19 20:20:12

标签: rust

我想要TcpStreamBufReader共享的BufWriter,我在以下位置找到了解决方案:
If BufReader takes ownership of a stream, how can I read and write lines on it?

现在我想要它自己的数据结构,但是我只能从以下部分得到答案:
Why can't I store a value and a reference to that value in the same struct?

所需的实现违反了所有权规则。

use std::io::{BufReader, BufWriter};
use std::net::TcpStream;

pub struct BufTcpStream<'a> {
    _socket: TcpStream,
    input:  BufReader<&'a TcpStream>;
    output: BufWriter<&'a TcpStream>;
}

impl<'a> BufTcpStream<'a> {
    pub fn new(socket: TcpStream) -> Self {
        Self{
            input : BufReader::new(&socket),
            output: BufWriter::new(&socket),
            _socket: socket,//                 <-- MOVE OF BORROWED VALUE HERE
        }
    }
}

要解决此问题,我必须确保TcpStream引用在整个结构生存期内均保持有效,我使用了Pin<Box<TcpStream>>来确保它有效。

但是编译器仍然抱怨借入值socket的移动。为了消除此障碍,我使用了std::meme::transmute()

现在,我想知道的是:

此实现安全吗?

use std::io::{BufReader, BufWriter};
use std::net::TcpStream;
use std::pin::Pin;

pub struct BufTcpStream<'a> {
    _socket: Pin<Box<TcpStream>>,
    input : BufReader<&'a TcpStream>,
    output: BufWriter<&'a TcpStream>,
}

impl<'a> BufTcpStream<'a> {
    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(&mut self) -> (
        &mut BufReader<&'a TcpStream>,
        &mut BufWriter<&'a TcpStream>
    ) {
        (&mut self.input, &mut self.output)
    }
}

2 个答案:

答案 0 :(得分:1)

使用TcpStream::try_clone获得第二个视频流:

  

返回的TcpStream是对该对象引用的同一流的引用。 两个句柄都将读取和写入相同的数据流,并且在一个流上设置的选项将传播到另一个流。

然后,您可以将一个包装在读者中,将一个包装在作者中:

use std::{
    io::{self, BufReader, BufWriter},
    net::TcpStream,
};

struct BufTcpStream {
    input: BufReader<TcpStream>,
    output: BufWriter<TcpStream>,
}

impl BufTcpStream {
    fn new(stream: TcpStream) -> io::Result<Self> {
        let input = BufReader::new(stream.try_clone()?);
        let output = BufWriter::new(stream);

        Ok(Self { input, output })
    }
}

另请参阅:

答案 1 :(得分:-1)

否。此实现不安全。有两个原因。

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)TcpStreamBufReaderBufWriter之前被删除

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))
        }
    }
}