You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Here's another take on enabling a storage-backed Allocator trait with minimal changes to the current implementation. This is inspired by @matthieu-m's awesome storage-poc (see also #79).
Locally, I've prototyped a rough Box and Vec implementation along with several Allocator impls to gain a base level of assurance. However, I'm somewhat new to both Rust and this feature so I might have overlooked key issues. Please also let me know if I have or if this (or a similar) proposal has already been considered and rejected/deferred.
Objectives
Enable building the following:
static_box::Box - inline-only allocation of unsized boxed items
SmallVec - inline/heap fallback allocation (both union and enum variants)
ThinArc - Pointee::Metadata (and potentially other items) stored at the start of the allocation (with only a thin pointer exposed)
Do not leak pointers to unstable memory locations
Do not depend on GAT stabilization
Zero cost abstraction (pay only for what you use)
Require minimal changes to the current proposal
Keep Allocator trait object safe (this was only partially successful - Allocator<Buffer = NonNull<u8>> is object safe, which appears to still meet the spirit of the request in Runtime allocators #83)
Issues
Currently it faces the same limitation as @matthieu-m's storage-poc: the Box implementation is not coercible because a type can't effectively implement CoerceUnsized while containing <T as Pointee>::Metadata. This issue is being discussed in rust-lang's #81513.
While not blocking, it would reduce the required constraints in data structure implementations if Pointee::Metadata required Metadata<Self>.
Proposal
// There are a few key changes:// - Associated type Buffer.// - Because Self::Buffer has no direct concept of its own layout, each (re-)allocation also// returns a Layout. Callers can leverage this to learn how much extra space (if any) was// allocated.// - Because Self::Buffer is uniquely-owned, it consume it on (de-)reallocation. Relatedly, it// is also returned in the Err on reallocation failure.//// SAFETY concerns for this trait should match those of the current trait.pubtraitAllocator{typeBuffer:Buffer;fnallocate(&self,layout:Layout) -> Result<(Self::Buffer,Layout),AllocError>;unsafefndeallocate(&self,buffer:Self::Buffer,layout:Layout);unsafefngrow(&self,buffer:Self::Buffer,old_layout:Layout,new_layout:Layout,) -> Result<(Self::Buffer,Layout),Self::Buffer>;unsafefnshrink(&self,buffer:Self::Buffer,old_layout:Layout,new_layout:Layout,) -> Result<(Self::Buffer,Layout),Self::Buffer>;// OMIT: other methods like *_zeroed, as_ref...}// From initial prototyping (and some godbolt usage), this appears to be inlined quite successfully// - in keeping with the goal of zero cost abstractions. Let me know if anyone foresees issues// with this, though.pubtraitBuffer{// SAFETY: Proven<T, M>::layout() must 'fit' the buffer (see Allocator trait for 'memory fit').unsafefnas_mut<T: ?Sized,M:Metadata<T>>(&mutself,metadata:Proven<T,M>) -> &mutT;// SAFETY: Proven<T, M>::layout() must 'fit' the buffer (see Allocator trait for 'memory fit').unsafefnas_ref<T: ?Sized,M:Metadata<T>>(&self,metadata:Proven<T,M>) -> &T;// Used in Allocator implementations - particularly for copying from one buffer to another// during reallocation in an intermediate/fallback Allocator.//// SAFETY: The pointer is only valid during F's invocation.unsafefnwith_ptr<F:FnOnce(NonNull<u8>)>(&mutself,layout:Layout,apply:F);}// This trait is strictly additive and can be added later (or not at all). It is used to build// things like ThinArc in which the Metadata is stored at the start of the allocation. Using this// trait we can (1) constrain allocators to have ThinBuffers and (2) read the sized "head"/metadata// out of the buffer without knowing the layout of the buffer.//// These methods are not included in Buffer because not all Buffer implementations will support// them. Consider a union-based Buffer (like the one used in SmallVec's union feature). In order// to know which union variant is valid, we must first know the capacity (metadata) of the Buffer.// That said, most Buffer implementations will likely be capable of implementing this trait.pubtraitThinBuffer:Buffer{unsafefnas_mut<T>(&mutself) -> &mutT;unsafefnas_ref<T>(&self) -> &T;}mod metadata {// Provides layout and metadata information for a given type. Note the following:// - Might not contain a valid layout (this can happen if size overflows on slice metadata).// - Might be a type other than T::Metadata (e.g., a Vec with max 256 elements might store// allocation's size/capacity in a u8).pubtraitMetadata<T: ?Sized + Pointee>:Copy{fntry_layout(self) -> Result<Layout,LayoutError>;fnmetadata(self) -> T::Metadata;}// Wraps Metadata<T> which have a known valid Layout. Consequently, subsequent layout checks can// be unchecked. The key is that this type can only be constructed via conversion from valid-// Layout metadata sources.pubstructProven<T: ?Sized,M:Copy>(M,PhantomData<fn(T) -> T>);impl<T: ?Sized,M:Metadata<T>>Proven<T,M>{pubfnfatten(self,ptr:NonNull<()>) -> NonNull<T>{NonNull::from_raw_parts(ptr,self.0.metadata())}pubfnlayout(self) -> Layout{unsafe{self.0.try_layout().unwrap_unchecked()}}}// OMIT: From impls for Proven to convert from &<T as Pointee> and Layout where// T: Sized + Pointee<Metadata = usize>}
PS: I am not good at naming. My dog's fairly lucky that he didn't end up named "Dog". Please let me know if you have any suggestions regarding better names.
Here's another take on enabling a storage-backed Allocator trait with minimal changes to the current implementation. This is inspired by @matthieu-m's awesome storage-poc (see also #79).
Locally, I've prototyped a rough Box and Vec implementation along with several Allocator impls to gain a base level of assurance. However, I'm somewhat new to both Rust and this feature so I might have overlooked key issues. Please also let me know if I have or if this (or a similar) proposal has already been considered and rejected/deferred.
Objectives
Allocatortrait object safe (this was only partially successful -Allocator<Buffer = NonNull<u8>>is object safe, which appears to still meet the spirit of the request in Runtime allocators #83)Issues
Currently it faces the same limitation as @matthieu-m's storage-poc: the Box implementation is not coercible because a type can't effectively implement
CoerceUnsizedwhile containing<T as Pointee>::Metadata. This issue is being discussed in rust-lang's #81513.While not blocking, it would reduce the required constraints in data structure implementations if
Pointee::MetadatarequiredMetadata<Self>.Proposal
PS: I am not good at naming. My dog's fairly lucky that he didn't end up named "Dog". Please let me know if you have any suggestions regarding better names.