Skip to main content

Allocator

Trait Allocator 

Source
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 { ... }
}
🔬This is a nightly-only experimental API. (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, or Arc containing an allocator is equivalent to the allocator inside.
  • All Global allocator instances are equivalent with each other.
  • All System allocator 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, or shrink methods, 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 deallocate call, or when it is passed as an argument to a grow, grow_zeroed or shrink call that returns Ok.
  • 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 &mut access.
    • 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 &mut access 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:

§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§

Source

fn allocate(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (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.

Source

unsafe fn deallocate(&self, ptr: NonNull<u8>, layout: Layout)

🔬This is a nightly-only experimental API. (allocator_api #32838)

Deallocates the memory referenced by ptr.

§Safety
  • ptr must denote a block of memory currently allocated via this allocator, and
  • layout must fit that block of memory.

Provided Methods§

Source

fn allocate_zeroed(&self, layout: Layout) -> Result<NonNull<[u8]>, AllocError>

🔬This is a nightly-only experimental API. (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.

Source

unsafe 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)

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
  • ptr must denote a block of memory currently allocated via this allocator.
  • old_layout must fit that block of memory (The new_layout argument need not fit it.).
  • new_layout.size() must be greater than or equal to old_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.

Source

unsafe 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)

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_size will either be preserved or zeroed, depending on the allocator implementation. old_size refers to the size of the memory block prior to the grow_zeroed call, which may be larger than the size that was originally requested when it was allocated.
  • Bytes old_size..new_size are zeroed. new_size refers to the size of the memory block returned by the grow_zeroed call.
§Safety
  • ptr must denote a block of memory currently allocated via this allocator.
  • old_layout must fit that block of memory (The new_layout argument need not fit it.).
  • new_layout.size() must be greater than or equal to old_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.

Source

unsafe 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)

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
  • ptr must denote a block of memory currently allocated via this allocator.
  • old_layout must fit that block of memory (The new_layout argument need not fit it.).
  • new_layout.size() must be smaller than or equal to old_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.

Source

fn by_ref(&self) -> &Self
where Self: Sized,

🔬This is a nightly-only experimental API. (allocator_api #32838)

Creates a “by reference” adapter for this instance of Allocator.

The returned adapter also implements Allocator and will simply borrow this.

Dyn Compatibility§

This trait is dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§

Source§

impl<A> Allocator for &A
where A: Allocator + ?Sized,

Source§

impl<A> Allocator for &mut A
where A: Allocator + ?Sized,