How to push an item into a Vec and continue using it?

Viewed 2325

I am trying to add an item to a Vec<T>, call a function and await it. I am still wrapping my head around the ownership and borrow rules. I understand the below won't work:

pub async fn begin_session(&mut self, stream: TcpStream) {
    let session = Session::new(stream);
    self.sessions.push(session);
            
    session.start_receiver().await; // obviously, won't work
}

The solution that I came up with below doesn't seem ideal (or thread-safe) to me:

pub async fn begin_session(&mut self, stream: TcpStream) {
    let session = Session::new(stream);
    self.sessions.push(session);

    let session = self.sessions.last().unwrap();
    
    session.start_receiver().await; // That works.
}

Is there a function in Vec<T> that does a push(T) and also returns T?

2 Answers

just doesn't seem ideal (or thread-safe) to me

It might not be ideal if the optimizer does not realize that the unwrap() will always be successful.

However, thread-safety does not play a role here: if you are planning to share the Vec or your Self later on, you will have to take care of that somehow.

Is there a function in Vec that does a push(T) and then also return T?

If it were there, then such a method would most likely return a &T or a &mut T which you can then copy or clone if needed. Other people may prefer to return the index instead. There are many possibilities depending on what you exactly need.

Whatever is best for your project, remember that you can add the method yourself to Vec. For instance:

trait PushReturn<T> {
    fn push_return(&mut self, t: T) -> &mut T;
}

impl<T> PushReturn<T> for Vec<T> {
    fn push_return(&mut self, t: T) -> &mut T {
        self.push(t);
        self.last_mut().unwrap()
    }
}

This seems like a good case in which to use reference-counted objects in the form of std::rc::Rc.

Your code might end up looking something like this:

pub async fn begin_session(&mut self, stream: TcpStream) {
    let session = Rc::new(Session::new(stream));
    self.sessions.push(Rc::clone(&session));

    &session.start_receiver().await;
}

When you clone an Rc, you're just cloning the reference to the object, and increasing the reference count. You're not cloning the object itself.

Disclaimer: I, too, am just learning Rust. It would be good to get a second opinion on this.

Related