With the Tracking issue for RFC 2580 being implemented (in nightly), it is now possible to create custom handles:
struct Handle<T>(u32, <T as Pointee>::Metadata);
And using those handles, one can implement Storages (as per storage-poc
), which are a generalization of allocators unlocking:
- Inline storage: storing the elements of a collection within the memory blob of the collection.
- Special memory storages: such as placing a
HashMap<T>
in shared memory. - ...
However, as I noted in this comment on the track issue, the current combination of CoerceUnsized
and Pointee
does not make it possible for Handle
itself to be CoerceUnsized
.
And I think it should be.
The goal of storage-poc
is to demonstrate that it should be possible to have the collections in collections
NOT depend on the alloc
crate. And for the purpose of writing collections, having a "manual" coerce
method is not too problematic -- it's all internal, after all.
For the purpose of porting Box
to storages, though, it's a stringent limitation. Look at this testcase:
#[test]
fn slice_storage() {
let storage = SingleElement::<[u8; 4]>::new();
let mut boxed: RawBox<[u8], _> = RawBox::new([1u8, 2, 3], storage)
.unwrap()
.coerce();
assert_eq!([1u8, 2, 3], &*boxed);
boxed[2] = 4;
assert_eq!([1u8, 2, 4], &*boxed);
}
Let's check the types here:
- I'll alias
SingleElement<[u8; 4]>
asStorage
for simplicity, it's a type of 4 bytes, aligned on a 1-byte boundary. -
RawBox<[u8; N], Storage>
is a type of 4 bytes, aligned on a 1-byte boundary. No heap allocation whatsoever. -
RawBox<[u8], Storage>
is a type of 16 bytes, aligned on a 8-bytes boundary, as it contains both aStorage
andusize
(the meta-data for the slice).
It's quite amazing that the extra .coerce()
works, allowing type-erasure, but let's honest it'd be sweeter if it was unnecessary, just like with a regular Box
.
Thus, I think we should have a mechanism to have compiler-provided unsizing of custom handle, and this my motivational example: making RawBox
first-class.
The canny reader will note that RawBox
with an inline storage provides in-place type-erasure, how cool is that? Cool enough to be a first-class citizen, I'll argue.
I feel obligated to note that I am NOT proposing a user-defined coercion logic.
A possible implementation, today, would be to have a strongly-typed Metadata
for Sized
types, and have the compiler automatically provide a CoerceUnsized
implementation to the slice/trait metadata as appropriate as is done with pointer types. Ralf pointed out that this may not make sense; I'll respectfully disagree and reserve the right to revise my opinion.