Shrink the size of a `Ref` in memory for some `B: ByteSlice`
- Dominant language
- Rust
- Stars
- 2.6k
- Forks
- 179
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 29
Description
Currently `Ref` is defined as:
https://github.com/google/zerocopy/blob/52ffa4d1cbd81d0570276d20ddac783ba2a5ecf2/src/lib.rs#L1417
This has the consequence that, even if `T: Sized`, when in principle `Ref` could be a single-word thin pointer, `Ref` is still as large as `B`. Usually this is two words (e.g. for `&[u8]`), but sometimes it's more (e.g. for [`core::cell::Ref`](https://doc.rust-lang.org/std/cell/struct.Ref.html), which has a reference count).
For some types, this is unavoidable. E.g., no matter what we do, we need to store the entire `core::cell::Ref` - if we didn't, we'd be leaking memory. But for some types - such as `&[u8]` - we can reconstruct the original `B: ByteSlice` just from a pointer to `T`.
Thus, I propose the following addition to `ByteSlice`:
```rust
trait ByteSlice {
// Name to be bikeshedded later. We may not want to tie the name to the `Ref`
// type since we plan on maybe making `ByteSlice` unsealed at some point.
//
// For `&[u8]`/`&mut [u8]`, this is just `NonNullFoo`. For `core::cell::Ref`/
// `core::cell::RefMut`, this is just `Self`.
type Foo: AsRef<[u8]> + AsMut<[u8]>;
// Either discards `foo`, constructing `Foo` from `self` (e.g. for `core::cell::Ref`),
// or discards `self`, constructing `Foo` from `foo` (e.g. for `&[u8]`).
//
// SAFETY: Caller promises that `ptr` references a sequence of bytes which are
// all initialized. That ensures that we can implement `AsRef<[u8]>`/`AsMut<[u8]>`
// without a `T: AsBytes` bound.
unsafe fn into_foo(self, foo: NonNullFoo) -> Self::Foo;
}
struct NonNullFoo {
// Invariant: Always references a fully-initialized sequences of bytes. This is fine
// because this is always constructed from a `B: ByteSlice` during `Ref` construction.
inner: NonNull,
}
// NOTE: Caller needs to ensure the `NonNullFoo` doesn't live for too long since
// these methods are not `usnafe`. Might need to be a safety requirement on
// `ByteSlice::into_foo`.
impl AsRef<[u8]> for NonNullFoo { ... }
impl AsMut<[u8]> for NonNullFoo { ... }
```
Then, we update `Ref`:
```rust
struct Ref(::Foo, PhantomData);
```
cc @kupiakos , I know you were interested in having this, and I remember you mentioned taking a stab at some ideas for how to implement it
Contributor guide
Assessment
This issue has not been assessed yet.