How to store objects that own lifetime references?

I'm calling split on a tokio::net::TcpStream connection. The problem is that I don't know how to store the stream and sink, because both hold a reference to the TcpStream:

use futures::prelude::*;

pub type MessageStream = Pin<Box<dyn Stream<Item = Result<rtsp_types::Message<Body>, ReadError>> + Send>>

pub type MessageSink = Pin<Box<dyn Sink<rtsp_types::Message<Body>, Error = std::io::Error> + Send>>

pub async fn connect(
    &mut self,
    addr: std::net::SocketAddr,
) -> std::result::Result<(), ClientError> {
    let connection = tokio::net::TcpStream::connect(addr).await;
    match connection {
        Ok(mut connection) => {
            let (read, write) = connection.split();

            let stream = super::message_socket::async_read(read, super::MAX_MESSAGE_SIZE);
            let sink = super::message_socket::async_write(write);

            let stream: MessageStream = Box::pin(stream);
            let sink: MessageSink = Box::pin(sink);

            self.stream = Some(stream);
            self.sink = Some(sink);
            Ok(())
        }
        Err(err) => Err(ClientError::SocketAcceptError(err)),
    }
}


pub(crate) fn async_write<W: tokio::io::AsyncWrite + Unpin + Send>(
    write: W,
) -> impl Sink<Message<Body>, Error = std::io::Error> + Send;

pub(crate) fn async_read<R: tokio::io::AsyncRead + Unpin + Send>(
    read: R,
    max_size: usize,
) -> impl Stream<Item = Result<Message<Body>, ReadError>> + Send;

I solved this by doing

let (read, write) = connection.into_split();

which, instead of split:

pub fn split<'a>(&'a mut self) -> (ReadHalf<'a>, WriteHalf<'a>)

allocates the ReadHalf and WriteHalf on the heap:

pub fn into_split(self) -> (OwnedReadHalf, OwnedWriteHalf)

However, what if there were no into_split method? How would I solve this?

I think that by specifying that the TcpStream lifetime is 'static, it'd solve, but what if I want to create lots of TcpStream connections? They would all accumulate on RAM?

I'd have my struct something like this:

pub struct Client {
    stream: Option<MessageStream<'a>>,
    sink: Option<MessageSink<'a>>,

but I don't have idea about the lifetime of the TcpStream.

If there was no into_split, what you are doing here would more or less not be possible. There's a reason that into_split exists.

but it is possible to not have dangling references with this setup, I guess.

What if I store both connection and sink and stream inside the client? Then both stream and sink always point to valid data because they are destructed together with connection

You can't have a struct where one field has a reference to another field. This is called a self-referential struct, and they are not possible.

1 Like

The struct could be moved and that would invalidate the references.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.