pub struct Discriminant { /* private fields */ }Expand description
Type describing the layout, alignment, and type of a container
Discriminant is central to the safety and performance of local pooling. It
describes 2 things in just 8 bytes.
-
The unique location in the source code of the implementation of IsoPoolable. This is accomplished by a proc macro that generates a global table of unique location ids for cross crate source code locations. This unique id ensures that different container types can’t be mixed in the same pool.
-
The layout and alignment of all the type parameters of the container. Discriminant has 3 slots that can be filled with either type parameters or const SIZE parameters. If your container has more parameters than that then you can’t locally pool it, and you can’t implement IsoPoolable. If you try you will likely cause undefined behavior.
In order to squeeze all this information into just 8 bytes there are some limitations.
-
You can’t have more than 0xFFFF implementations of IsoPoolable in the same project. This includes all the crates depended on by the project.
-
Your type parameters must have size <= 0x0FFF bytes and alignment of 1, 2, 4, 8, or 16. Alignments > 16 will be rejected.
-
const SIZE parameters must be <= 0x7FFF.
If any of these constraints are violated the Discriminant constructors
will return None. If you desire you may panic at that point to cause a
compile error. If you do not panic and instead leave DISCRIMINANT as
None then local pool operations on that type will work just fine, but
nothing will be pooled. Objects will be freed when they are dropped and
take will allocate new objects each time it is called.
§Discriminant Collisions and Why They’re Safe
Two different types can have the same discriminant if they have the same size and alignment. For example:
#[repr(C)]
struct Padded1 { a: u8, _pad: [u8; 7], b: u64 } // size 16, align 8
#[repr(C)]
struct Padded2 { x: u64, y: u64 } // size 16, align 8If you pool Vec<Padded1> and Vec<Padded2>, they would get the same discriminant
because Padded1 and Padded2 have identical size and alignment. This means a
Vec<Padded1> allocation could be reused as a Vec<Padded2> allocation.
This is safe because:
- Containers are always empty when returned to pools (
reset()ensures this) - An empty
Vec<T>only cares aboutT’s size and alignment for its allocation - The actual bit patterns inside
Tdon’t matter when the Vec is empty - When you take from the pool and populate it with your type, it’s initialized correctly
The discriminant system is designed to ensure that different container types never
share pools (via the LocationId), and that the memory layout of type parameters
is compatible. As long as containers are properly emptied before pooling (which reset()
guarantees), the system is memory safe even with discriminant collisions.
Implementations§
Source§impl Discriminant
impl Discriminant
Sourcepub const fn empty(id: LocationId) -> Discriminant
pub const fn empty(id: LocationId) -> Discriminant
return a new empty discriminant
Sourcepub const fn new(id: LocationId) -> Option<Discriminant>
pub const fn new(id: LocationId) -> Option<Discriminant>
build a discriminant for a type with no type variables (just a location id). Always returns Some
Sourcepub const fn add_param<T>(self) -> Option<Self>
pub const fn add_param<T>(self) -> Option<Self>
Add a type parameter.
Discriminant has 3 slots. Each slot can hold either a type parameter or a const SIZE. This will return None if the discriminant is full, or the type parameter’s size or alignment are too big.
Sourcepub const fn add_size<const SIZE: usize>(self) -> Option<Self>
pub const fn add_size<const SIZE: usize>(self) -> Option<Self>
Add a const SIZE
Discriminant has 3 slots. Each slot can hold either a type parameter or a const SIZE. This will return None if the discriminant is full, or if the size is too large.
Sourcepub const fn new_p1<T>(id: LocationId) -> Option<Discriminant>
pub const fn new_p1<T>(id: LocationId) -> Option<Discriminant>
build a discriminant with one type param
Sourcepub const fn new_p1_size<T, const SIZE: usize>(
id: LocationId,
) -> Option<Discriminant>
pub const fn new_p1_size<T, const SIZE: usize>( id: LocationId, ) -> Option<Discriminant>
build a discriminant with one type param and a size
Sourcepub const fn new_p2<T, U>(id: LocationId) -> Option<Discriminant>
pub const fn new_p2<T, U>(id: LocationId) -> Option<Discriminant>
build a discriminant with two type params
Sourcepub const fn new_p2_size<T, U, const SIZE: usize>(
id: LocationId,
) -> Option<Discriminant>
pub const fn new_p2_size<T, U, const SIZE: usize>( id: LocationId, ) -> Option<Discriminant>
build a discriminant with two type params and a size
Sourcepub const fn new_p3<T, U, V>(id: LocationId) -> Option<Discriminant>
pub const fn new_p3<T, U, V>(id: LocationId) -> Option<Discriminant>
build a discriminant with three type params
Trait Implementations§
Source§impl Clone for Discriminant
impl Clone for Discriminant
Source§fn clone(&self) -> Discriminant
fn clone(&self) -> Discriminant
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for Discriminant
impl Debug for Discriminant
Source§impl Hash for Discriminant
impl Hash for Discriminant
Source§impl PartialEq for Discriminant
impl PartialEq for Discriminant
impl Copy for Discriminant
impl Eq for Discriminant
impl StructuralPartialEq for Discriminant
Auto Trait Implementations§
impl Freeze for Discriminant
impl RefUnwindSafe for Discriminant
impl Send for Discriminant
impl Sync for Discriminant
impl Unpin for Discriminant
impl UnwindSafe for Discriminant
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.