Hello! I'm trying to implement scoped append-only typed arena whose types have references to itself. Everything works fine without reusing initial buffer, but I'm having trouble finding a solution that reuses it. The issue looks like standard rust self-referential struct problem but not exactly. Here is the code: Rust Playground
Borrow checking can't reason about self-referential types (since they form cycles), so they're forbidden in safe code.
Creation of self-referential types requires unsafe, and there's no correct lifetime annotation for them.
You must also be very careful about anything that can make a &mut reference to self-referential types, even temporarily, because &mut implies exclusivity through its mere existence (even if not dereferenced) and guarantees that there are no other usable references to the same object.
You also need to be careful about running Drop of such types when the arena is torn down. They can use their references while being dropped, but could end up referencing an already-dropped object from the same arena. This is why bumpalo forgets the contents.
That’s coming remarkably close to working with only the standard library.
I’m looking for possible crates that could fill the gap… maybe something like this could help that cast_slice method would help your case a lot. Edit: on second look, it seems like its covariance alone can support this use-case, too.[1] Also note that I have never reviewed this crate; but the basic idea of this type seems sound, and the crate has a decent number of users.
Sure, Drop needs some care. But my current demand is actually only for Copy types to implement similar to GPU algorithms like "use some memory for computations during one epoch, and then forget whole data".
So, actually I'm asking about how can we deal with invariance that appears for my example type Data<'t> as @steffahn noted.
Yeah, it works, it is so strange. Therefore what are we missing in std? Something like safe transmuting &mut [Data<'static>] into &mut [Data<'_>]? (for real use cases we could ignore MaybeUninit for now because initialization is needed only once)
As you can see in my initial code, I tried to did this kind of transmute by abusing allocation-free vec collect specialization. But invariance didn't let me do this in opposite direction.
Edit: I realized that in fact MaybeUninit is important too to leave everything unitialized for safe rust after another scoped use.
Presumably the standard library might be a bit more hesitant to offer this exact capability because the implementation (at least as is offered with without_alloc) turns a mismatch of layout (size or alignment) into run-time errors, even though for many types, the exact size and alignment is not a stable guarantee.
If only a lifetime changes, it isn't an issue of course as that can't affect the layout.
It already has [T]::align_to_mut; I expect that a safe wrapper would have essentially the same shape, but require MaybeUninit for the transmute-flavored parts.
Oh, I haven't noticed that method got a lot stronger documentation. It used to say things such as
The method may make the middle slice the greatest length possible for a given type and input slice, but only your algorithm’s performance should depend on that, not its correctness. It is permissible for all of the input data to be returned as the prefix or suffix slice.