3

I am trying to send a message containing Cow<[u8]> over a channel. However, the lifetime rules do not allow me to pass it over.

use std::borrow::Cow;
use std::sync::mpsc;

#[derive(Debug, Default, PartialEq, Clone)]
pub struct PlayerAction<'a> {
    pub data: Cow<'a, [u8]>,
}

#[derive(Debug, Clone)]
pub enum NetworkMessage<'a> {
    PlayerActionMessage(PlayerAction<'a>),
}

pub struct ConnectionsManager<'a> {
    channel: mpsc::Sender<NetworkMessage<'a>>,
}

pub struct MessageHandler<'a> {
    pub connection_manager: ConnectionsManager<'a>,
}

fn read_message<'a>(bytes: &'a Vec<u8>) -> NetworkMessage {
    NetworkMessage::PlayerActionMessage(PlayerAction {
        data: Cow::Borrowed(&bytes),
    })
}

impl<'a> MessageHandler<'a> {
    fn on_message(&mut self, msg: Vec<u8>) {
        let readm = read_message(&msg);
        self.connection_manager.channel.send(readm);
    }
}

fn main() {}

Playground

error[E0597]: `msg` does not live long enough
  --> src/main.rs:30:35
   |
30 |         let readm = read_message(&msg);
   |                                   ^^^ borrowed value does not live long enough
31 |         self.connection_manager.channel.send(readm);
32 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 28:1...
  --> src/main.rs:28:1
   |
28 | impl<'a> MessageHandler<'a> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^

MessageHandler outlives Vec<u8>, but I have no idea how to pass it any other way.

Is there any way to pass Vec<u8> so that it can live longer than the on_message function?

trent
  • 25,033
  • 7
  • 51
  • 90
mikbal
  • 1,168
  • 1
  • 16
  • 27
  • 2
    See also [Why is it discouraged to accept a reference to a String (&String), Vec (&Vec) or Box (&Box) as a function argument?](https://stackoverflow.com/q/40006219/155423) – Shepmaster Sep 13 '18 at 14:08
  • 1
    How do you expect this code to work? `msg` is destroyed at the end of `on_message`, but a reference to it could still be waiting in the channel. You'd then have a reference to invalid memory. **Rust prevented you from introducing memory unsafety**, that's the point of Rust. – Shepmaster Sep 13 '18 at 14:12
  • on_message method is from TCP handler trait. I have to work with Vec. But i also need Cow<[u8]> because it is generated serialization code. – mikbal Sep 13 '18 at 14:17
  • Why are you using a reference to the `msg` instead of putting the owned value in the `Cow`? – Shepmaster Sep 13 '18 at 14:19

1 Answers1

5

Here's a one-line change you can make to make this code correct:

fn on_message(&mut self, msg: &'a [u8])

Instead of taking a Vec<u8> by value, take a reference to a slice that is already guaranteed to live at least as long as 'a. This makes it the caller's responsibility to ensure that whatever you pass to on_message will live long enough to be sent over the channel.


But maybe you can't do that. Either the caller will have the same problem, and you can't push it up any farther, or the Vec<u8> is a required part of on_message's signature. If that's the case, you will have to change read_message. Here's one possibility:

fn read_message<'b>(bytes: Vec<u8>) -> NetworkMessage<'b> {
    NetworkMessage::PlayerActionMessage(PlayerAction {
        data: Cow::Owned(bytes),
    })
}

impl<'a> MessageHandler<'a> {
    fn on_message(&mut self, msg: Vec<u8>) {
        let readm = read_message(msg);
        self.connection_manager.channel.send(readm);
    }
}

Moving msg into read_message leaves the compiler free to pick any lifetime it wants for 'b. In on_message it can just pick 'a to make the code compile. The disadvantage of this approach is that you may have to .clone() the Vec if you need to use it again after sending it down the channel.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
trent
  • 25,033
  • 7
  • 51
  • 90
  • 1
    I think your `read_message` should be `-> NetworkMessage<'static>` for clarity. – Shepmaster Sep 13 '18 at 14:53
  • @Shepmaster I'm thinking about it. In this case, `'static` would indeed be equivalent. But if `NetworkMessage<'b>` were invariant in `'b`, returning `NetworkMessage<'static>` would be more limiting. – trent Sep 13 '18 at 15:05
  • Thank you for the detailed explanation. I will modify the serialization lib to my needs. Quick question, is there any way to bypass borrow checkier using unsafe blocks? – mikbal Sep 13 '18 at 17:26
  • 1
    @mikbal Sometimes, kind of? Instead of references, you can use raw pointers, which do not have lifetimes. But you still have to solve the *problem* of lifetimes, which is the same as in C or C++ without the handrails provided by the borrow checker. For example, [*don't do this*](https://play.rust-lang.org/?gist=b31ade589371e7162771cdeb0911d2d3&version=stable&mode=debug&edition=2015), because it compiles, but has undefined behavior, as Shepmaster pointed out in the question comments. To use `unsafe` correctly, you must be smarter than the compiler. – trent Sep 13 '18 at 17:54
  • @trentcl So i can't call memcpy on &msg and pass the copy to channel? – mikbal Sep 13 '18 at 21:15
  • @mikbal You can use `memcpy` (or its Rust equivalent [`ptr::copy`](https://doc.rust-lang.org/std/ptr/fn.copy.html)), but that doesn't solve the problem. `memcpy` doesn't allocate, so you're going to have to get that destination buffer from somewhere. Is it local? It will be invalid as soon as `on_message` returns. Do you allocate it on the heap? That's essentially the same as cloning the `Vec`, so you may as well do it the (in Rust) normal, safe way. – trent Sep 13 '18 at 22:43
  • 1
    You can also safely write `read_message` so that it does the copy internally: [I would use `to_vec`](https://play.rust-lang.org/?gist=a4ad22e5c213ab6bccb0c47e2e8d8733&version=stable&mode=debug&edition=2015). Notice that `bytes` no longer has a `'a` in its type; that's how you tell the compiler that the output lifetime is meant to be independent of `bytes`. (And yes, that can also be written as `fn read_message(bytes: &[u8]) -> NetworkMessage<'static>`.) – trent Sep 13 '18 at 22:52