Repository navigation
Allocator traits and std::heap #32838
Copy link
Copy link
Closed
Labels
A-allocatorsArea: Custom and system allocatorsArea: Custom and system allocatorsB-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.Blocker: Approved by a merged RFC but not yet implemented.B-unstableBlocker: Implemented in the nightly compiler and unstable.Blocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCI-libs-radarLibs issues that are tracked on the team's radar.Libs 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.Status: It's hard to tell what's been done and what hasn't! Someone should do some investigation.T-langRelevant to the language teamRelevant to the language teamT-libsRelevant to the library team, which will review and decide on the PR/issue.Relevant to the library team, which will review and decide on the PR/issue.
Description
📢 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.
- land
struct Layout,trait Allocator, and default implementations inalloccrate (Allocator integration #42313) - decide where parts should live (e.g. default impls has dependency on
alloccrate, butLayout/Allocatorcould be inlibcore...) (Allocator integration #42313) - fixme from source code: audit default implementations (in
Layoutfor overflow errors, (potentially switching to overflowing_add and overflowing_mul as necessary). - decide if
realloc_in_placeshould be replaced withgrow_in_placeandshrink_in_place(comment) (Allocator integration #42313) - review arguments for/against associated error type (see subthread here)
- determine what the requirements are on the alignment provided to
fn dealloc. (See discussion on allocator rfc and global allocator rfc and traitAllocPR.)- Is it required to deallocate with the exact
alignthat you allocate with? Concerns have been raised that allocators like jemalloc don't require this, and it's difficult to envision an allocator that does require this. (more discussion). @ruuda and @rkruppe look like they've got the most thoughts so far on this.
- Is it required to deallocate with the exact
- should
AllocErrbeErrorinstead? (comment) - Is it required to deallocate with the exact size that you allocate with? With the
usable_sizebusiness we may wish to allow, for example, that you if you allocate with(size, align)you must deallocate with a size somewhere in the range ofsize...usable_size(size, align). It appears that jemalloc is totally ok with this (doesn't require you to deallocate with a precisesizeyou allocate with) and this would also allowVecto naturally take advantage of the excess capacity jemalloc gives it when it does an allocation. (although actually doing this is also somewhat orthogonal to this decision, we're just empoweringVec). So far @gankro has most of the thoughts on this. (@alexcrichton believes this was settled in Allocator integration #42313 due to the definition of "fits") - similar to previous question: Is it required to deallocate with the exact alignment that you allocated with? (See comment from 5 June 2017)
- OSX/
alloc_systemis buggy on huge alignments (e.g. an align of1 << 32) if huge requested align, alloc_system heap::allocate on OS X returns unaligned values #30170 Add precondition toLayoutthat thealignfit in a u32. #43217 - should
Layoutprovide afn stride(&self)method? (See also Separate size and stride for types rfcs#1397, Collapse trailing padding #17027 ) -
Allocator::ownsas a method? Alloc: Add owns method #44302
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 {
// ...
}Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
A-allocatorsArea: Custom and system allocatorsArea: Custom and system allocatorsB-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.Blocker: Approved by a merged RFC but not yet implemented.B-unstableBlocker: Implemented in the nightly compiler and unstable.Blocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCI-libs-radarLibs issues that are tracked on the team's radar.Libs 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.Status: It's hard to tell what's been done and what hasn't! Someone should do some investigation.T-langRelevant to the language teamRelevant to the language teamT-libsRelevant to the library team, which will review and decide on the PR/issue.Relevant to the library team, which will review and decide on the PR/issue.
Activity
gereeter commentedon Apr 11, 2016
I unfortunately wasn't paying close enough attention to mention this in the RFC discussion, but I think that
realloc_in_placeshould be replaced by two functions,grow_in_placeandshrink_in_place, for two reasons:reallocorrealloc_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.realloc_in_placeis quite small, usinggrowandshrinkbetter 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,
reallocwould probably also want to be split intogrowandsplit, but the only advantage to having an overloadablereallocfunction that I know of is to be able to usemmap's remap option, which does not have such a distinction.gereeter commentedon Apr 11, 2016
Additionally, I think that the default implementations of
reallocandrealloc_in_placeshould be slightly adjusted - instead of checking against theusable_size,reallocshould just first try torealloc_in_place. In turn,realloc_in_placeshould 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 improvingrealloc_in_place. However, the default performance ofreallocdoes not suffer, as the check against theusable_sizeis still performed.pnkfelix commentedon Oct 26, 2016
Another issue: The doc for
fn realloc_in_placesays that if it returns Ok, then one is assured thatptrnow "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_inplacefunction implies that it will perform any such check.fn realloc_in_placewill 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_placereally be burdened with checking that the alignment of the givenptris compatible with that ofnew_layout? It is probably better in this case (of this one method) to push that requirement back to the caller...pnkfelix commentedon Oct 26, 2016
@gereeter you make good points; I will add them to the check list I am accumulating in the issue description.
pnkfelix commentedon Oct 31, 2016
(at this point I am waiting for
#[may_dangle]support to ride the train into thebetachannel so that I will then be able to use it for std collections as part of allocator integration)joshlf commentedon Jan 4, 2017
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 anAllocatorand 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?
Ericson2314 commentedon Jan 4, 2017
I don't think this has been discussed yet.
You could write your own
ObjectAllocator<T>, and then doimpl<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.
nikomatsakis commentedon Jan 4, 2017
@pnkfelix
I guess this has happened?
556 remaining items
SimonSapin commentedon Jul 14, 2026
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.
Add `Allocator::shrink`: what `Vec::shrink_to_fit` actually asks for
nia-e commentedon Sep 22, 2026
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
Rollup merge of rust-lang#156882 - nia-e:stable-allocator, r=clarfonthey
Unrolled build for #156882 in rollup 163189
Qix- commentedon Sep 23, 2026
Will this go out as part of 1.100, per chance?
RalfJung commentedon Sep 23, 2026
If nothing goes wrong, then yes it will.
Sewer56 commentedon Sep 23, 2026
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)
judeVector commentedon Sep 24, 2026
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!
Rollup merge of #156882 - nia-e:stable-allocator, r=clarfonthey
Rollup merge of rust-lang#156882 - nia-e:stable-allocator, r=clarfonthey
Rollup merge of rust-lang#156882 - nia-e:stable-allocator, r=clarfonthey