Make `struct` act as `&mut [u8]` buffer

Here's an example code:

    let eb = EventBuffer::default();

    loop {
        if let Some(event) = eb.next() {
            // ...
        } else {
            let size = eb.awaiting_bytes_size();
            let mut buf = vec![0u8; size];
            stream.read_exact(&mut buf).await.unwrap();
            eb.append(&mut buf);
        }
    }

I want EventBuffer to act as a "vector" that could be passed directly to the read_exact method and the buffer size would thus be dynamically set by the EventBuffer itself. The end result would be this:

    let eb = EventBuffer::default();

    loop {
        if let Some(event) = eb.next() {
            // ...
        } else {
            stream.read_exact(&mut eb).await.unwrap(); // fill eb of size defined by the EventBuffer
        }
    }

Would something like this be possible in Rust?

You can do this using the zerocopy crate. Derive the AsBytes trait on it, then use the as_bytes_mut method that AsBytes defines for you.

You can also do it without extra crates if you use unsafe. That would amount to this:

fn as_bytes_mut(data: &mut EventBuffer) -> &mut [u8] {
    let ptr = data as *mut EventBuffer as *mut u8;
    let len = std::mem::size_of::<EventBuffer>();
    unsafe { std::slice::from_raw_parts(ptr, len) }
}

However, this unsafe code is only correct if the conditions listed on AsBytes in the zerocopy crate are satisfied. The advantage of using the crate is that it doesn't compile if the conditions arent satisfied.

If the conditions aren't satisfied, then it's not possible. (The unsafe code would still compile, but it is wrong in that case.)

1 Like

Hum, but isn't this kind of similar in terms that you have to use as_butes_mut()?

struct EventBuffer {
    size: usize,
    inner: Vec<u8>,
}
impl EventBuffer {
    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
        self.size += 1;
        self.inner = vec![0u8; self.size];
        &mut self.inner
    }  
}
impl Default for EventBuffer {
    fn default() -> Self {
        Self {
            size: 0,
            inner: vec![0u8; 0],
        }
    }
}
stream.read(&mut eb.as_bytes_mut()).await.unwrap();
stream.read(&mut eb.as_bytes_mut()).await.unwrap();
...

So ... how to make a struct to have such properties already and be able to use as

stream.read(&mut eb).await.unwrap();

Oh, in that case I misunderstood. If you implement the bytes::BufMut trait, then you can use it with Tokio's read_buf method.

In theory, you could implement Deref and DerefMut, to have exactly the behavior you've specified - playground.
In practice, this would be extremely confusing for anyone reading your code and error-prone for yourself, so Alice's advice is much better.

1 Like

This is it! I haven't used Deref and DerefMut before.

Thank you both @Cerber-Ursi, @alice.