How to pass `&mut BytesMut` to a function that takes `&Bytes`?

How can we pass &mut BytesMut to a function that takes &Bytes?

Both types are from the bytes crate.

I have a function parse_len(bytes: &Bytes) that parses length of a received message and it takes &Bytes. The first byte represents the type of a message, and the following bytes represent its length in bytes, and the length part ends with \r\n. Length is encoded as an arbitrary number of decimal digits. So, my function returns a tuple of the decoded/parsed message length and the number of bytes read to decode/parse the length. So, it doesn't need to take &mut BytesMut, and I have a lot of other functions that rely on &Bytes and not on &mut BytesMut, and I wouldn't like to change them, if possible. All those functions just read from the provided bytes and they don't need to mutate them.

I now want to include tokio-util::codec in the mix and I have a problem.

Its Decoder::decode() method takes src: &mut BytesMut and I have to pass src as input to my parse_len(bytes: &Bytes) function.

The error I'm getting is: arguments to this function are incorrect: expected &Bytes, found &mut BytesMut .

I haven't been able to find a workaround.

This issue is about removing mutability from &mut BytesMut, but it can't be done trivially, such as with &*, as &_, or similar.

Also, avoiding copying would be welcome as it obviously affects performance. The thing is we don't know in advance how long the sequence that represents length is. Sure, we could search for \r\n, but it all affects performance, as parse_len() will do it again. Is there a (simple) way to remove mutability from &mut BytesMut to &Bytes?

Can this issue be solved and how?

1 Like

If you change the bytes argument of parse_len from &Bytes to &[u8], you can re-borrow &mut BytesMut as &[u8], thanks to BytesMut Deref<Target=[u8]> implementation:

use bytes::BytesMut;

fn parse_len(_: &[u8]) {}

fn foo(b: &mut BytesMut) {
    parse_len(&b);
}

fn main() {
    let mut b = BytesMut::from("foo");
    
    foo(&mut b);
}

Playground.

6 Likes

How does that work performance-wise?

We're generally using the bytes crate for performance reasons, as its operations just increment the reference count.

Will your solution affect performance? I believe it shouldn't (by much) as we just de-reference and then re-borrow, and I don't see any copying involved. Is this the best we can do?

There is no copying of the underlying data when we dereference BytesMut as &[u8].

1 Like

That's what I thought.

Alright, thanks!

1 Like

If you don't need the additional Bytes functionality, then I agree with @jofas that &[u8] is the better, more general way to go.

But you can convert MutBytes to Bytes at presumably almost no cost, and also convert it back (probably at almost no cost for your use case[1]).


  1. and there's also this method if you wanted more control â†Šī¸Ž

2 Likes

I considered this, too, but ultimately thought &[u8] was nicer, given that OP passes around &mut BytesMut and not BytesMut directly. They would have to pass it by value instead, or clone it to get an owned version. The latter would involve a copy of the underlying data.

3 Likes

I agree, if &[u8] is all that's needed, it's definitely superior.

2 Likes

@quinedot , thanks!

I tried this conversion to no avail:

parse_len(&Bytes::from(src)) yields the trait std::convert::From<&mut bytes::BytesMut> is not implemented for bytes::Bytes.

Do you know why?

Dereferencing src yields cannot move and suggests cloning it, but that would surely affect performance and I don't see a reason to do it in this case.

It's what was mentioned here:

...or clone or split or some other redesign that makes sense for your use case.

If you don't need Bytes specifically, you should just ignore my side note and go with &[u8].

1 Like