pub unsafe trait Allocator {
// Required methods
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>;
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout);
// Provided methods
fn allocate_zeroed(
&self,
layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ... }
unsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ... }
unsafe fn grow_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ... }
unsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError> { ... }
fn by_ref(&self) -> &Self
where Self: Sized { ... }
}allocator_api #32838)Expand description
An implementation of Allocator can allocate, grow, shrink, and deallocate arbitrary blocks of
data described via Layout.
Allocator is designed to be implemented on ZSTs, references, or smart pointers.
An allocator for MyAlloc([u8; N]) cannot be moved, without updating the pointers to the
allocated memory.
In contrast to GlobalAlloc, Allocator allows zero-sized allocations. If an underlying
allocator does not support this (like jemalloc) or responds by returning a null pointer
(such as libc::malloc), this must be caught by the implementation.
§Equivalent allocators
Multiple allocator values can sometimes be interchangeable with each other. When this is the case, we refer to those allocators as being equivalent to each other.
The following conditions are sufficient conditions for allocators to be equivalent.
- An allocator is equivalent to itself. (Equivalence is reflexive.)
- If an allocator is equivalent to a second allocator, then the second allocator is also equivalent to the first. (Equivalence is symmetric.)
- If an allocator is equivalent to a second allocator, and the second allocator is equivalent to a third allocator, then the first allocator is also equivalent to the third allocator. (Equivalence is transitive.)
- Moving, subtyping, unsize-coercing, or trait-upcasting an allocator does not change what the allocator is equivalent to.
- Copying or cloning allocator results in an allocator that’s equivalent to the initial allocator.
Additionally, implementors of Allocator may specify additional equivalences
between allocators. It is the responsibility of such implementors to make sure
that equivalent allocators have “compatible” Allocator implementations.
In particular, the standard library specifies the following equivalences:
- A reference to an allocator (either
&or&mut) is equivalent to the allocator being referenced. - A
Box,Rc, orArccontaining an allocator is equivalent to the allocator inside. - All
Globalallocator instances are equivalent with each other. - All
Systemallocator instances are equivalent with each other.
Note: Currently, the interaction between cloning and unsize-coercing allocators
is unsound, and there is ongoing discussion on how to revise the Allocator trait
to fix this. See #156920.
§Currently allocated memory
Some of the methods require that a memory block is currently allocated by some specific allocator. This means that:
- the starting address for that memory block was previously returned by
the
allocate,allocate_zeroed,grow,grow_zeroed, orshrinkmethods, called on an allocator that’s equivalent to this specific allocator; and - the memory block has not subsequently been invalidated.
§Invalidating memory blocks
A memory block that is currently allocated becomes invalidated when one of the following happens:
- The memory block is deallocated. This occurs when the memory block
is passed as an argument to a
deallocatecall, or when it is passed as an argument to agrow,grow_zeroedorshrinkcall that returnsOk. - All (equivalent) allocators that this memory block is allocated with,
each has one of the following happen to them:
- The allocator’s destructor runs.
- The allocator is mutated through public API taking
&mutaccess. - One of the borrow-checker lifetimes in the allocator’s type expires.
Note that these conditions imply that a collection may ensure that any specific currently allocated memory block won’t be invalidated, by:
- not deallocating that memory block,
- owning an allocator that memory block is allocated with, and
- not publicly exposing
&mutaccess to that allocator.
Also note that safe public API of an allocator with & access is not
allowed to invalidate its memory blocks. Furthermore, unsafe public API
of an allocator with & access must document that they invalidate
memory blocks (e.g., by calling deallocate) if they do. Therefore,
collections may safely expose & access to its allocator.
Also note that, even in cases where are other “alive” allocators known to be
equivalent to a given collection’s allocator, most collections still should
not publicly expose &mut access to its allocator. The fact that there are
other “alive” allocators would prevent this &mut access from invalidating
the collection’s memory block, but public &mut access is still likely to
be unsound, since a user could replace the collection’s allocator with
a non-equivalent allocator, causing the collection to deallocate its memory
with the wrong allocator.
§Memory fitting
Some of the methods require that a layout fit a memory block or vice versa. This means that the
following conditions must hold:
- the memory block must be currently allocated with alignment of
layout.align(), and layout.size()must fall in the rangemin ..= max, where:minis the size of the layout used to allocate the block, andmaxis the actual size returned fromallocate,allocate_zeroed,grow,grow_zeroed, orshrink.
§Safety
Implementors of Allocator must ensure that a memory block that
is currently allocated by the allocator points to valid memory,
until that memory block is invalidated. The implementor must also
not violate this invariant of Allocator via allocator equivalences
that are in the implementor’s control (e.g., via a misbehaving
impl Clone for Box<MyAllocator>).
Additionally, any memory block returned by the allocator must
satisfy the allocation invariants described in core::ptr.
In particular, if a block has base address p and size n,
then p as usize + n <= usize::MAX must hold.
This ensures that pointer arithmetic within the allocation
(for example, ptr.add(len)) cannot overflow the address space.
Required Methods§
Sourcefn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>
🔬This is a nightly-only experimental API. (allocator_api #32838)
fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>
allocator_api #32838)Attempts to allocate a block of memory.
On success, returns a NonNull<[u8]> meeting the size and alignment guarantees of layout.
The returned block may have a larger size than specified by layout.size(), and may or may
not have its contents initialized.
Note that the returned block of memory is considered currently allocated
with this allocator (and equivalent allocators).
Therefore, it is the responsibility of implementors of Allocator to make sure that
this block of memory points to valid memory until the block is invalidated
§Errors
Returning Err indicates that either memory is exhausted or layout does not meet
allocator’s size or alignment constraints.
Implementations are encouraged to return Err on memory exhaustion rather than panicking or
aborting, but this is not a strict requirement. (Specifically: it is legal to implement
this trait atop an underlying native allocation library that aborts on memory exhaustion.)
Clients wishing to abort computation in response to an allocation error are encouraged to
call the handle_alloc_error function, rather than directly invoking panic! or similar.
Sourceunsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout)
🔬This is a nightly-only experimental API. (allocator_api #32838)
unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout)
allocator_api #32838)Deallocates the memory referenced by ptr.
§Safety
ptrmust denote a block of memory currently allocated via this allocator, andlayoutmust fit that block of memory.
Provided Methods§
Sourcefn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>
🔬This is a nightly-only experimental API. (allocator_api #32838)
fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>
allocator_api #32838)Behaves like allocate, but also ensures that the returned memory is zero-initialized.
§Errors
Returning Err indicates that either memory is exhausted or layout does not meet
allocator’s size or alignment constraints.
Implementations are encouraged to return Err on memory exhaustion rather than panicking or
aborting, but this is not a strict requirement. (Specifically: it is legal to implement
this trait atop an underlying native allocation library that aborts on memory exhaustion.)
Clients wishing to abort computation in response to an allocation error are encouraged to
call the handle_alloc_error function, rather than directly invoking panic! or similar.
Sourceunsafe fn grow(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError>
🔬This is a nightly-only experimental API. (allocator_api #32838)
unsafe fn grow( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>
allocator_api #32838)Attempts to extend the memory block.
Returns a new NonNull<[u8]> containing a pointer and the actual size of the allocated
memory. The pointer is suitable for holding data described by new_layout. To accomplish
this, the allocator may extend the allocation referenced by ptr to fit the new layout.
If this returns Ok, then the memory block referenced by ptr has been invalidated.
The old ptr must not be used to access the memory, even if the allocation was grown in-place.
The newly returned pointer is the only valid pointer for accessing this memory now.
If this method returns Err, then the memory block has not been invalidated,
and the contents of the memory block are unaltered.
§Safety
ptrmust denote a block of memory currently allocated via this allocator.old_layoutmust fit that block of memory (Thenew_layoutargument need not fit it.).new_layout.size()must be greater than or equal toold_layout.size().
Note that new_layout.align() need not be the same as old_layout.align().
§Errors
Returns Err if the new layout does not meet the allocator’s size and alignment
constraints of the allocator, or if growing otherwise fails.
Implementations are encouraged to return Err on memory exhaustion rather than panicking or
aborting, but this is not a strict requirement. (Specifically: it is legal to implement
this trait atop an underlying native allocation library that aborts on memory exhaustion.)
Clients wishing to abort computation in response to an allocation error are encouraged to
call the handle_alloc_error function, rather than directly invoking panic! or similar.
Sourceunsafe fn grow_zeroed(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError>
🔬This is a nightly-only experimental API. (allocator_api #32838)
unsafe fn grow_zeroed( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>
allocator_api #32838)Behaves like grow, but also ensures that the new contents are set to zero before being
returned.
The memory block will contain the following contents after a successful call to
grow_zeroed:
- Bytes
0..old_layout.size()are preserved from the original allocation. - Bytes
old_layout.size()..old_sizewill either be preserved or zeroed, depending on the allocator implementation.old_sizerefers to the size of the memory block prior to thegrow_zeroedcall, which may be larger than the size that was originally requested when it was allocated. - Bytes
old_size..new_sizeare zeroed.new_sizerefers to the size of the memory block returned by thegrow_zeroedcall.
§Safety
ptrmust denote a block of memory currently allocated via this allocator.old_layoutmust fit that block of memory (Thenew_layoutargument need not fit it.).new_layout.size()must be greater than or equal toold_layout.size().
Note that new_layout.align() need not be the same as old_layout.align().
§Errors
Returns Err if the new layout does not meet the allocator’s size and alignment
constraints of the allocator, or if growing otherwise fails.
Implementations are encouraged to return Err on memory exhaustion rather than panicking or
aborting, but this is not a strict requirement. (Specifically: it is legal to implement
this trait atop an underlying native allocation library that aborts on memory exhaustion.)
Clients wishing to abort computation in response to an allocation error are encouraged to
call the handle_alloc_error function, rather than directly invoking panic! or similar.
Sourceunsafe fn shrink(
&self,
ptr: NonNull<u8>,
old_layout: Layout,
new_layout: Layout,
) -> Result<NonNull<[u8]>, AllocError>
🔬This is a nightly-only experimental API. (allocator_api #32838)
unsafe fn shrink( &self, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout, ) -> Result<NonNull<[u8]>, AllocError>
allocator_api #32838)Attempts to shrink the memory block.
Returns a new NonNull<[u8]> containing a pointer and the actual size of the allocated
memory. The pointer is suitable for holding data described by new_layout. To accomplish
this, the allocator may shrink the allocation referenced by ptr to fit the new layout.
If this returns Ok, then the memory block referenced by ptr has been invalidated.
The old ptr must not be used to access the memory, even if the allocation was shrunk in-place.
The newly returned pointer is the only valid pointer for accessing this memory now.
If this method returns Err, then the memory block has not been invalidated,
and the contents of the memory block are unaltered.
§Safety
ptrmust denote a block of memory currently allocated via this allocator.old_layoutmust fit that block of memory (Thenew_layoutargument need not fit it.).new_layout.size()must be smaller than or equal toold_layout.size().
Note that new_layout.align() need not be the same as old_layout.align().
§Errors
Returns Err if the new layout does not meet the allocator’s size and alignment
constraints of the allocator, or if shrinking otherwise fails.
Implementations are encouraged to return Err on memory exhaustion rather than panicking or
aborting, but this is not a strict requirement. (Specifically: it is legal to implement
this trait atop an underlying native allocation library that aborts on memory exhaustion.)
Clients wishing to abort computation in response to an allocation error are encouraged to
call the handle_alloc_error function, rather than directly invoking panic! or similar.
Dyn Compatibility§
This trait is dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".