Skip to content

Allocator traits and std::heap #32838

Closed
Closed

Description

@nikomatsakis

View all comments

📢 This feature has a dedicated working group, please direct comments and concerns to the working group's repo.

The remainder of this post is no longer an accurate summary of the current state; see that dedicated working group instead.

Old content

Original Post:


FCP proposal: #32838 (comment)
FCP checkboxes: #32838 (comment)


Tracking issue for rust-lang/rfcs#1398 and the std::heap module.

State of std::heap after #42313:

pub struct Layout { /* ... */ }

impl Layout {
    pub fn new<T>() -> Self;
    pub fn for_value<T: ?Sized>(t: &T) -> Self;
    pub fn array<T>(n: usize) -> Option<Self>;
    pub fn from_size_align(size: usize, align: usize) -> Option<Layout>;
    pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout;

    pub fn size(&self) -> usize;
    pub fn align(&self) -> usize;
    pub fn align_to(&self, align: usize) -> Self;
    pub fn padding_needed_for(&self, align: usize) -> usize;
    pub fn repeat(&self, n: usize) -> Option<(Self, usize)>;
    pub fn extend(&self, next: Self) -> Option<(Self, usize)>;
    pub fn repeat_packed(&self, n: usize) -> Option<Self>;
    pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)>;
}

pub enum AllocErr {
    Exhausted { request: Layout },
    Unsupported { details: &'static str },
}

impl AllocErr {
    pub fn invalid_input(details: &'static str) -> Self;
    pub fn is_memory_exhausted(&self) -> bool;
    pub fn is_request_unsupported(&self) -> bool;
    pub fn description(&self) -> &str;
}

pub struct CannotReallocInPlace;

pub struct Excess(pub *mut u8, pub usize);

pub unsafe trait Alloc {
    // required
    unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>;
    unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout);

    // provided
    fn oom(&mut self, _: AllocErr) -> !;
    fn usable_size(&self, layout: &Layout) -> (usize, usize);
    unsafe fn realloc(&mut self,
                      ptr: *mut u8,
                      layout: Layout,
                      new_layout: Layout) -> Result<*mut u8, AllocErr>;
    unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>;
    unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr>;
    unsafe fn realloc_excess(&mut self,
                             ptr: *mut u8,
                             layout: Layout,
                             new_layout: Layout) -> Result<Excess, AllocErr>;
    unsafe fn grow_in_place(&mut self,
                            ptr: *mut u8,
                            layout: Layout,
                            new_layout: Layout) -> Result<(), CannotReallocInPlace>;
    unsafe fn shrink_in_place(&mut self,
                              ptr: *mut u8,
                              layout: Layout,
                              new_layout: Layout) -> Result<(), CannotReallocInPlace>;

    // convenience
    fn alloc_one<T>(&mut self) -> Result<Unique<T>, AllocErr>
        where Self: Sized;
    unsafe fn dealloc_one<T>(&mut self, ptr: Unique<T>)
        where Self: Sized;
    fn alloc_array<T>(&mut self, n: usize) -> Result<Unique<T>, AllocErr>
        where Self: Sized;
    unsafe fn realloc_array<T>(&mut self,
                               ptr: Unique<T>,
                               n_old: usize,
                               n_new: usize) -> Result<Unique<T>, AllocErr>
        where Self: Sized;
    unsafe fn dealloc_array<T>(&mut self, ptr: Unique<T>, n: usize) -> Result<(), AllocErr>
        where Self: Sized;
}

/// The global default allocator
pub struct Heap;

impl Alloc for Heap {
    // ...
}

impl<'a> Alloc for &'a Heap {
    // ...
}

/// The "system" allocator
pub struct System;

impl Alloc for System {
    // ...
}

impl<'a> Alloc for &'a System {
    // ...
}

Activity

  1. added
    B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.
    T-langRelevant to the language team
    T-libs-api[DEPRECATED; DO NOT USE]
    B-unstableBlocker: Implemented in the nightly compiler and unstable.
    on Apr 8, 2016
  2. gereeter commented on Apr 11, 2016

    @gereeter
    Contributor

    I unfortunately wasn't paying close enough attention to mention this in the RFC discussion, but I think that realloc_in_place should be replaced by two functions, grow_in_place and shrink_in_place, for two reasons:

    • I can't think of a single use case (short of implementing realloc or realloc_in_place) where it is unknown whether the size of the allocation is increasing or decreasing. Using more specialized methods makes it slightly more clear what is going on.
    • The code paths for growing and shrinking allocations tend to be radically different - growing involves testing whether adjacent blocks of memory are free and claiming them, while shrinking involves carving off properly sized subblocks and freeing them. While the cost of a branch inside realloc_in_place is quite small, using grow and shrink better captures the distinct tasks that an allocator needs to perform.

    Note that these can be added backwards-compatibly next to realloc_in_place, but this would constrain which functions would be by default implemented in terms of which others.

    For consistency, realloc would probably also want to be split into grow and split, but the only advantage to having an overloadable realloc function that I know of is to be able to use mmap's remap option, which does not have such a distinction.

  3. gereeter commented on Apr 11, 2016

    @gereeter
    Contributor

    Additionally, I think that the default implementations of realloc and realloc_in_place should be slightly adjusted - instead of checking against the usable_size, realloc should just first try to realloc_in_place. In turn, realloc_in_place should by default check against the usable size and return success in the case of a small change instead of universally returning failure.

    This makes it easier to produce a high-performance implementation of realloc: all that is required is improving realloc_in_place. However, the default performance of realloc does not suffer, as the check against the usable_size is still performed.

  4. pnkfelix commented on Oct 26, 2016

    @pnkfelix
    Contributor

    Another issue: The doc for fn realloc_in_place says that if it returns Ok, then one is assured that ptr now "fits" new_layout.

    To me this implies that it must check that the alignment of the given address matches any constraint implied by new_layout.

    However, I don't think the spec for the underlying fn reallocate_inplace function implies that it will perform any such check.

    • Furthermore, it seems reasonable that any client diving into using fn realloc_in_place will themselves be ensuring that the alignments work (in practice I suspect it means that the same alignment is required everywhere for the given use case...)

    So, should the implementation of fn realloc_in_place really be burdened with checking that the alignment of the given ptr is compatible with that of new_layout? It is probably better in this case (of this one method) to push that requirement back to the caller...

  5. pnkfelix commented on Oct 26, 2016

    @pnkfelix
    Contributor

    @gereeter you make good points; I will add them to the check list I am accumulating in the issue description.

  6. pnkfelix commented on Oct 31, 2016

    @pnkfelix
    Contributor

    (at this point I am waiting for #[may_dangle] support to ride the train into the beta channel so that I will then be able to use it for std collections as part of allocator integration)

  7. joshlf commented on Jan 4, 2017

    @joshlf
    Contributor

    I'm new to Rust, so forgive me if this has been discussed elsewhere.

    Is there any thought on how to support object-specific allocators? Some allocators such as slab allocators and magazine allocators are bound to a particular type, and do the work of constructing new objects, caching constructed objects which have been "freed" (rather than actually dropping them), returning already-constructed cached objects, and dropping objects before freeing the underlying memory to an underlying allocator when required.

    Currently, this proposal doesn't include anything along the lines of ObjectAllocator<T>, but it would be very helpful. In particular, I'm working on an implementation of a magazine allocator object-caching layer (link above), and while I can have this only wrap an Allocator and do the work of constructing and dropping objects in the caching layer itself, it'd be great if I could also have this wrap other object allocators (like a slab allocator) and truly be a generic caching layer.

    Where would an object allocator type or trait fit into this proposal? Would it be left for a future RFC? Something else?

  8. Ericson2314 commented on Jan 4, 2017

    @Ericson2314
    Contributor

    I don't think this has been discussed yet.

    You could write your own ObjectAllocator<T>, and then do impl<T: Allocator, U> ObjectAllocator<U> for T { .. }, so that every regular allocator can serve as an object-specific allocator for all objects.

    Future work would be modifying collections to use your trait for their nodes, instead of plain ole' (generic) allocators directly.

  9. nikomatsakis commented on Jan 4, 2017

    @nikomatsakis
    ContributorAuthor

    @pnkfelix

    (at this point I am waiting for #[may_dangle] support to ride the train into the beta channel so that I will then be able to use it for std collections as part of allocator integration)

    I guess this has happened?

  10. 556 remaining items

  11. SimonSapin commented on Jul 14, 2026

    @SimonSapin
    Contributor

    Good point that pointer stability is a separate concern from the cost of (internal) memcpy. It may be worth at least clarifying docs about the intended behavior/constraints, but again better in a dedicated issue than in this thread.

  12. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
  13. nia-e commented on Sep 22, 2026

    @nia-e
    Member

    The main points of this issue will be stabilised by #156882, so I believe this can - at long last - be closed as completed. Future work will happen at #163177 & related

  14. added a commit that references this issue on Sep 23, 2026
  15. added a commit that references this issue on Sep 23, 2026
  16. Qix- commented on Sep 23, 2026

    @Qix-

    Will this go out as part of 1.100, per chance?

  17. RalfJung commented on Sep 23, 2026

    @RalfJung
    Member

    If nothing goes wrong, then yes it will.

    Image
  18. Sewer56 commented on Sep 23, 2026

    @Sewer56

    Is this the moment we party?
    👉👈 💜

    Oh golly- it has been so long.
    I might even need to go update the guide from the other post of mine 😅
    #32838 (comment)

  19. judeVector commented on Sep 24, 2026

    @judeVector

    This is huge!, Can’t wait to get my hands on this. It opens up a whole new level of flexibility around memory allocation in Rust, especially for specialized use cases and performance-critical applications. Excited to explore what people will build with it!

  20. added a commit that references this issue on Oct 1, 2026
  21. added a commit that references this issue on Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-allocatorsArea: Custom and system allocatorsB-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.B-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCI-libs-radarLibs issues that are tracked on the team's radar.S-tracking-needs-summaryStatus: It's hard to tell what's been done and what hasn't! Someone should do some investigation.T-langRelevant to the language teamT-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions