Bytes
can be transformed into Bytes<'a>
.
Bytes<'a>
is identical to current Bytes
, except when KIND_STATIC
, ptr
lifetime is 'a
, but not 'static
'. So Bytes<'static>
is identical to current Bytes
.
Why
Suppose you have Bytes
object. And you want, for example, split that Bytes
into several slices and pass these objects somewhere else, and you keep original Bytes
object. For example, you want to create a sequence of
struct MyProtocolFrame {
header: Header,
payload: Bytes,
}
and pass these objects down to serialization. You cannot change payload
type to &'a [u8]
, because sometimes MyProtocolFrame
must own data (in different part of project), and sometimes it could simply hold borrowed data.
Currently you can "borrow" with Bytes::slice
operation, which is expensive, because it:
- upgrades
Bytes
to KIND_ARC
when Bytes
is KIND_VEC
- does expensive atomic increment and decrement
If Bytes
is parameterized with lifetime, Bytes<'a>
could have another cheap operation like:
impl Bytes {
fn slice_ref<'a>(&'a self, from: usize, to: usize) -> Bytes<'a> {
Bytes::from_borrowed(self.as_ref()[from..to])
}
}
A little bonus
And as a little bonus, you can now create a borrowed Bytes
object from &[u8]
which is obtained from some library which doesn't used Bytes
. Sometimes that's convenient.
Drawback
The major problem with this design in ergonomics: users would have to write lifetimes a lot, and get lots of compiler errors, which is annoying.
I can think of a couple of ways to avoid this problem.
First, declare a type alias:
// Flexible type for advanced use cases
pub struct BytesMaybeBorrowed<'a> { ... }
// Identical to current Bytes
pub type Bytes = BytesMaybeBorrowed<'static>;
or
pub struct BytesMaybeBorrowed<'a> { ... }
pub struct Bytes(pub BytesMaybeBorrowed<'static>);
Second version seems to be easier to use, but it requires duplication of lots of functions. Which is fine by me.
What do you think?