Struct bumpalo::Bump

source ·
pub struct Bump { /* private fields */ }
Expand description

An arena to bump allocate into.

No Drops

Objects that are bump-allocated will never have their Drop implementation called — unless you do it manually yourself. This makes it relatively easy to leak memory or other resources.

If you have a type which internally manages

  • an allocation from the global heap (e.g. Vec<T>),
  • open file descriptors (e.g. std::fs::File), or
  • any other resource that must be cleaned up (e.g. an mmap)

and relies on its Drop implementation to clean up the internal resource, then if you allocate that type with a Bump, you need to find a new way to clean up after it yourself.

Potential solutions are:

Note that not calling Drop is memory safe! Destructors are never guaranteed to run in Rust, you can’t rely on them for enforcing memory safety.

Example

use bumpalo::Bump;

// Create a new bump arena.
let bump = Bump::new();

// Allocate values into the arena.
let forty_two = bump.alloc(42);
assert_eq!(*forty_two, 42);

// Mutable references are returned from allocation.
let mut s = bump.alloc("bumpalo");
*s = "the bump allocator; and also is a buffalo";

Allocation Methods Come in Many Flavors

There are various allocation methods on Bump, the simplest being alloc. The others exist to satisfy some combination of fallible allocation and initialization. The allocation methods are summarized in the following table:

Infallible Allocation Fallible Allocation
By Value alloc try_alloc
Infallible Initializer Function alloc_with try_alloc_with
Fallible Initializer Function alloc_try_with try_alloc_try_with

Fallible Allocation: The try_alloc_ Method Prefix

These allocation methods let you recover from out-of-memory (OOM) scenarioes, rather than raising a panic on OOM.

use bumpalo::Bump;

let bump = Bump::new();

match bump.try_alloc(MyStruct {
    // ...
}) {
    Ok(my_struct) => {
        // Allocation succeeded.
    }
    Err(e) => {
        // Out of memory.
    }
}

struct MyStruct {
    // ...
}

Initializer Functions: The _with Method Suffix

Calling one of the generic …alloc(x) methods is essentially equivalent to the matching …alloc_with(|| x). However if you use …alloc_with, then the closure will not be invoked until after allocating space for storing x on the heap.

This can be useful in certain edge-cases related to compiler optimizations. When evaluating for example bump.alloc(x), semantically x is first put on the stack and then moved onto the heap. In some cases, the compiler is able to optimize this into constructing x directly on the heap, however in many cases it does not.

The …alloc_with functions try to help the compiler be smarter. In most cases doing for example bump.try_alloc_with(|| x) on release mode will be enough to help the compiler realize that this optimization is valid and to construct x directly onto the heap.

Warning

These functions critically depend on compiler optimizations to achieve their desired effect. This means that it is not an effective tool when compiling without optimizations on.

Even when optimizations are on, these functions do not guarantee that the value is constructed on the heap. To the best of our knowledge no such guarantee can be made in stable Rust as of 1.54.

Fallible Initialization: The _try_with Method Suffix

The generic …alloc_try_with(|| x) methods behave like the purely _with suffixed methods explained above. However, they allow for fallible initialization by accepting a closure that returns a Result and will attempt to undo the initial allocation if this closure returns Err.

Warning

If the inner closure returns Ok, space for the entire Result remains allocated inside self. This can be a problem especially if the Err variant is larger, but even otherwise there may be overhead for the Result’s discriminant.

Undoing the allocation in the Err case always fails if f successfully made any additional allocations in self.

For example, the following will always leak also space for the Result into this Bump, even though the inner reference isn’t kept and the Err payload is returned semantically by value:

let bump = bumpalo::Bump::new();

let r: Result<&mut [u8; 1000], ()> = bump.alloc_try_with(|| {
    let _ = bump.alloc(0_u8);
    Err(())
});

assert!(r.is_err());

Since Err payloads are first placed on the heap and then moved to the stack, bump.…alloc_try_with(|| x)? is likely to execute more slowly than the matching bump.…alloc(x?) in case of initialization failure. If this happens frequently, using the plain un-suffixed method may perform better.

Bump Allocation Limits

bumpalo supports setting a limit on the maximum bytes of memory that can be allocated for use in a particular Bump arena. This limit can be set and removed with set_allocation_limit. The allocation limit is only enforced when allocating new backing chunks for a Bump. Updating the allocation limit will not affect existing allocations or any future allocations within the Bump’s current chunk.

Example
let bump = bumpalo::Bump::new();

assert_eq!(bump.allocation_limit(), None);
bump.set_allocation_limit(Some(0));

assert!(bump.try_alloc(5).is_err());

bump.set_allocation_limit(Some(6));

assert_eq!(bump.allocation_limit(), Some(6));

bump.set_allocation_limit(None);

assert_eq!(bump.allocation_limit(), None);
Warning

Because of backwards compatibility, allocations that fail due to allocation limits will not present differently than errors due to resource exhaustion.

Implementations§

source§

impl Bump

source

pub fn new() -> Bump

Construct a new arena to bump allocate into.

Example
let bump = bumpalo::Bump::new();
source

pub fn try_new() -> Result<Bump, AllocErr>

Attempt to construct a new arena to bump allocate into.

Example
let bump = bumpalo::Bump::try_new();
source

pub fn with_capacity(capacity: usize) -> Bump

Construct a new arena with the specified byte capacity to bump allocate into.

Example
let bump = bumpalo::Bump::with_capacity(100);
source

pub fn try_with_capacity(capacity: usize) -> Result<Self, AllocErr>

Attempt to construct a new arena with the specified byte capacity to bump allocate into.

Example
let bump = bumpalo::Bump::try_with_capacity(100);
source

pub fn allocation_limit(&self) -> Option<usize>

The allocation limit for this arena in bytes.

Example
let bump = bumpalo::Bump::with_capacity(0);

assert_eq!(bump.allocation_limit(), None);

bump.set_allocation_limit(Some(6));

assert_eq!(bump.allocation_limit(), Some(6));

bump.set_allocation_limit(None);

assert_eq!(bump.allocation_limit(), None);
source

pub fn set_allocation_limit(&self, limit: Option<usize>)

Set the allocation limit in bytes for this arena.

The allocation limit is only enforced when allocating new backing chunks for a Bump. Updating the allocation limit will not affect existing allocations or any future allocations within the Bump’s current chunk.

Example
let bump = bumpalo::Bump::with_capacity(0);

bump.set_allocation_limit(Some(0));

assert!(bump.try_alloc(5).is_err());
source

pub fn reset(&mut self)

Reset this bump allocator.

Performs mass deallocation on everything allocated in this arena by resetting the pointer into the underlying chunk of memory to the start of the chunk. Does not run any Drop implementations on deallocated objects; see the top-level documentation for details.

If this arena has allocated multiple chunks to bump allocate into, then the excess chunks are returned to the global allocator.

Example
let mut bump = bumpalo::Bump::new();

// Allocate a bunch of things.
{
    for i in 0..100 {
        bump.alloc(i);
    }
}

// Reset the arena.
bump.reset();

// Allocate some new things in the space previously occupied by the
// original things.
for j in 200..400 {
    bump.alloc(j);
}
source

pub fn alloc<T>(&self, val: T) -> &mut T

Allocate an object in this Bump and return an exclusive reference to it.

Panics

Panics if reserving space for T fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.alloc("hello");
assert_eq!(*x, "hello");
source

pub fn try_alloc<T>(&self, val: T) -> Result<&mut T, AllocErr>

Try to allocate an object in this Bump and return an exclusive reference to it.

Errors

Errors if reserving space for T fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.try_alloc("hello");
assert_eq!(x, Ok(&mut "hello"));
source

pub fn alloc_with<F, T>(&self, f: F) -> &mut Twhere F: FnOnce() -> T,

Pre-allocate space for an object in this Bump, initializes it using the closure, then returns an exclusive reference to it.

See The _with Method Suffix for a discussion on the differences between the _with suffixed methods and those methods without it, their performance characteristics, and when you might or might not choose a _with suffixed method.

Panics

Panics if reserving space for T fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.alloc_with(|| "hello");
assert_eq!(*x, "hello");
source

pub fn try_alloc_with<F, T>(&self, f: F) -> Result<&mut T, AllocErr>where F: FnOnce() -> T,

Tries to pre-allocate space for an object in this Bump, initializes it using the closure, then returns an exclusive reference to it.

See The _with Method Suffix for a discussion on the differences between the _with suffixed methods and those methods without it, their performance characteristics, and when you might or might not choose a _with suffixed method.

Errors

Errors if reserving space for T fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.try_alloc_with(|| "hello");
assert_eq!(x, Ok(&mut "hello"));
source

pub fn alloc_try_with<F, T, E>(&self, f: F) -> Result<&mut T, E>where F: FnOnce() -> Result<T, E>,

Pre-allocates space for a Result in this Bump, initializes it using the closure, then returns an exclusive reference to its T if Ok.

Iff the allocation fails, the closure is not run.

Iff Err, an allocator rewind is attempted and the E instance is moved out of the allocator to be consumed or dropped as normal.

See The _with Method Suffix for a discussion on the differences between the _with suffixed methods and those methods without it, their performance characteristics, and when you might or might not choose a _with suffixed method.

For caveats specific to fallible initialization, see The _try_with Method Suffix.

Errors

Iff the allocation succeeds but f fails, that error is forwarded by value.

Panics

Panics if reserving space for Result<T, E> fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.alloc_try_with(|| Ok("hello"))?;
assert_eq!(*x, "hello");
source

pub fn try_alloc_try_with<F, T, E>( &self, f: F ) -> Result<&mut T, AllocOrInitError<E>>where F: FnOnce() -> Result<T, E>,

Tries to pre-allocates space for a Result in this Bump, initializes it using the closure, then returns an exclusive reference to its T if all Ok.

Iff the allocation fails, the closure is not run.

Iff the closure returns Err, an allocator rewind is attempted and the E instance is moved out of the allocator to be consumed or dropped as normal.

See The _with Method Suffix for a discussion on the differences between the _with suffixed methods and those methods without it, their performance characteristics, and when you might or might not choose a _with suffixed method.

For caveats specific to fallible initialization, see The _try_with Method Suffix.

Errors

Errors with the Alloc variant iff reserving space for Result<T, E> fails.

Iff the allocation succeeds but f fails, that error is forwarded by value inside the Init variant.

Example
let bump = bumpalo::Bump::new();
let x = bump.try_alloc_try_with(|| Ok("hello"))?;
assert_eq!(*x, "hello");
source

pub fn alloc_slice_copy<T>(&self, src: &[T]) -> &mut [T]where T: Copy,

Copy a slice into this Bump and return an exclusive reference to the copy.

Panics

Panics if reserving space for the slice fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.alloc_slice_copy(&[1, 2, 3]);
assert_eq!(x, &[1, 2, 3]);
source

pub fn alloc_slice_clone<T>(&self, src: &[T]) -> &mut [T]where T: Clone,

Clone a slice into this Bump and return an exclusive reference to the clone. Prefer alloc_slice_copy if T is Copy.

Panics

Panics if reserving space for the slice fails.

Example
#[derive(Clone, Debug, Eq, PartialEq)]
struct Sheep {
    name: String,
}

let originals = [
    Sheep { name: "Alice".into() },
    Sheep { name: "Bob".into() },
    Sheep { name: "Cathy".into() },
];

let bump = bumpalo::Bump::new();
let clones = bump.alloc_slice_clone(&originals);
assert_eq!(originals, clones);
source

pub fn alloc_str(&self, src: &str) -> &mut str

Copy a string slice into this Bump and return an exclusive reference to it.

Panics

Panics if reserving space for the string fails.

Example
let bump = bumpalo::Bump::new();
let hello = bump.alloc_str("hello world");
assert_eq!("hello world", hello);
source

pub fn alloc_slice_fill_with<T, F>(&self, len: usize, f: F) -> &mut [T]where F: FnMut(usize) -> T,

Allocates a new slice of size len into this Bump and returns an exclusive reference to the copy.

The elements of the slice are initialized using the supplied closure. The closure argument is the position in the slice.

Panics

Panics if reserving space for the slice fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.alloc_slice_fill_with(5, |i| 5 * (i + 1));
assert_eq!(x, &[5, 10, 15, 20, 25]);
source

pub fn alloc_slice_fill_copy<T: Copy>(&self, len: usize, value: T) -> &mut [T]

Allocates a new slice of size len into this Bump and returns an exclusive reference to the copy.

All elements of the slice are initialized to value.

Panics

Panics if reserving space for the slice fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.alloc_slice_fill_copy(5, 42);
assert_eq!(x, &[42, 42, 42, 42, 42]);
source

pub fn alloc_slice_fill_clone<T: Clone>( &self, len: usize, value: &T ) -> &mut [T]

Allocates a new slice of size len slice into this Bump and return an exclusive reference to the copy.

All elements of the slice are initialized to value.clone().

Panics

Panics if reserving space for the slice fails.

Example
let bump = bumpalo::Bump::new();
let s: String = "Hello Bump!".to_string();
let x: &[String] = bump.alloc_slice_fill_clone(2, &s);
assert_eq!(x.len(), 2);
assert_eq!(&x[0], &s);
assert_eq!(&x[1], &s);
source

pub fn alloc_slice_fill_iter<T, I>(&self, iter: I) -> &mut [T]where I: IntoIterator<Item = T>, I::IntoIter: ExactSizeIterator,

Allocates a new slice of size len slice into this Bump and return an exclusive reference to the copy.

The elements are initialized using the supplied iterator.

Panics

Panics if reserving space for the slice fails, or if the supplied iterator returns fewer elements than it promised.

Example
let bump = bumpalo::Bump::new();
let x: &[i32] = bump.alloc_slice_fill_iter([2, 3, 5].iter().cloned().map(|i| i * i));
assert_eq!(x, [4, 9, 25]);
source

pub fn alloc_slice_fill_default<T: Default>(&self, len: usize) -> &mut [T]

Allocates a new slice of size len slice into this Bump and return an exclusive reference to the copy.

All elements of the slice are initialized to T::default().

Panics

Panics if reserving space for the slice fails.

Example
let bump = bumpalo::Bump::new();
let x = bump.alloc_slice_fill_default::<u32>(5);
assert_eq!(x, &[0, 0, 0, 0, 0]);
source

pub fn alloc_layout(&self, layout: Layout) -> NonNull<u8>

Allocate space for an object with the given Layout.

The returned pointer points at uninitialized memory, and should be initialized with std::ptr::write.

Panics

Panics if reserving space matching layout fails.

source

pub fn try_alloc_layout(&self, layout: Layout) -> Result<NonNull<u8>, AllocErr>

Attempts to allocate space for an object with the given Layout or else returns an Err.

The returned pointer points at uninitialized memory, and should be initialized with std::ptr::write.

Errors

Errors if reserving space matching layout fails.

source

pub fn chunk_capacity(&self) -> usize

Gets the remaining capacity in the current chunk (in bytes).

Example
use bumpalo::Bump;

let bump = Bump::with_capacity(100);

let capacity = bump.chunk_capacity();
assert!(capacity >= 100);
source

pub fn iter_allocated_chunks(&mut self) -> ChunkIter<'_>

Returns an iterator over each chunk of allocated memory that this arena has bump allocated into.

The chunks are returned ordered by allocation time, with the most recently allocated chunk being returned first, and the least recently allocated chunk being returned last.

The values inside each chunk are also ordered by allocation time, with the most recent allocation being earlier in the slice, and the least recent allocation being towards the end of the slice.

Safety

Because this method takes &mut self, we know that the bump arena reference is unique and therefore there aren’t any active references to any of the objects we’ve allocated in it either. This potential aliasing of exclusive references is one common footgun for unsafe code that we don’t need to worry about here.

However, there could be regions of uninitialized memory used as padding between allocations, which is why this iterator has items of type [MaybeUninit<u8>], instead of simply [u8].

The only way to guarantee that there is no padding between allocations or within allocated objects is if all of these properties hold:

  1. Every object allocated in this arena has the same alignment, and that alignment is at most 16.
  2. Every object’s size is a multiple of its alignment.
  3. None of the objects allocated in this arena contain any internal padding.

If you want to use this iter_allocated_chunks method, it is your responsibility to ensure that these properties hold before calling MaybeUninit::assume_init or otherwise reading the returned values.

Finally, you must also ensure that any values allocated into the bump arena have not had their Drop implementations called on them, e.g. after dropping a [bumpalo::boxed::Box<T>][crate::boxed::Box].

Example
let mut bump = bumpalo::Bump::new();

// Allocate a bunch of `i32`s in this bump arena, potentially causing
// additional memory chunks to be reserved.
for i in 0..10000 {
    bump.alloc(i);
}

// Iterate over each chunk we've bump allocated into. This is safe
// because we have only allocated `i32`s in this arena, which fulfills
// the above requirements.
for ch in bump.iter_allocated_chunks() {
    println!("Used a chunk that is {} bytes long", ch.len());
    println!("The first byte is {:?}", unsafe {
        ch[0].assume_init()
    });
}

// Within a chunk, allocations are ordered from most recent to least
// recent. If we allocated 'a', then 'b', then 'c', when we iterate
// through the chunk's data, we get them in the order 'c', then 'b',
// then 'a'.

bump.reset();
bump.alloc(b'a');
bump.alloc(b'b');
bump.alloc(b'c');

assert_eq!(bump.iter_allocated_chunks().count(), 1);
let chunk = bump.iter_allocated_chunks().nth(0).unwrap();
assert_eq!(chunk.len(), 3);

// Safe because we've only allocated `u8`s in this arena, which
// fulfills the above requirements.
unsafe {
    assert_eq!(chunk[0].assume_init(), b'c');
    assert_eq!(chunk[1].assume_init(), b'b');
    assert_eq!(chunk[2].assume_init(), b'a');
}
source

pub unsafe fn iter_allocated_chunks_raw(&self) -> ChunkRawIter<'_>

Returns an iterator over raw pointers to chunks of allocated memory that this arena has bump allocated into.

This is an unsafe version of iter_allocated_chunks(), with the caller responsible for safe usage of the returned pointers as well as ensuring that the iterator is not invalidated by new allocations.

Safety

Allocations from this arena must not be performed while the returned iterator is alive. If reading the chunk data (or casting to a reference) the caller must ensure that there exist no mutable references to previously allocated data.

In addition, all of the caveats when reading the chunk data from iter_allocated_chunks() still apply.

source

pub fn allocated_bytes(&self) -> usize

Calculates the number of bytes currently allocated across all chunks in this bump arena.

If you allocate types of different alignments or types with larger-than-typical alignment in the same arena, some padding bytes might get allocated in the bump arena. Note that those padding bytes will add to this method’s resulting sum, so you cannot rely on it only counting the sum of the sizes of the things you’ve allocated in the arena.

The allocated bytes do not include the size of bumpalo’s metadata, so the amount of memory requested from the Rust allocator is higher than the returned value.

Example
let bump = bumpalo::Bump::new();
let _x = bump.alloc_slice_fill_default::<u32>(5);
let bytes = bump.allocated_bytes();
assert!(bytes >= core::mem::size_of::<u32>() * 5);
source

pub fn allocated_bytes_including_metadata(&self) -> usize

Calculates the number of bytes requested from the Rust allocator for this Bump.

This number is equal to the allocated_bytes() plus the size of the bump metadata.

Trait Implementations§

source§

impl Debug for Bump

source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
source§

impl Default for Bump

source§

fn default() -> Bump

Returns the “default value” for a type. Read more
source§

impl Drop for Bump

source§

fn drop(&mut self)

Executes the destructor for this type. Read more
source§

impl Send for Bump

Auto Trait Implementations§

§

impl !RefUnwindSafe for Bump

§

impl !Sync for Bump

§

impl Unpin for Bump

§

impl !UnwindSafe for Bump

Blanket Implementations§

source§

impl<T> Any for Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for Twhere T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for Twhere T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for Twhere U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for Twhere U: TryFrom<T>,

§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.