Stack-allocated Arrays

Stack-allocated arrays are arrays that are stored on the stack, as opposed to the heap. The stack is a region of memory that is used for storing local variables and function call information. Stack allocation has several important characteristics:

  • Allocating and deallocating memory on the stack is very fast, because it simply involves adjusting the stack pointer. There is no need for complex memory management as with heap allocation.
  • The size of stack-allocated arrays must be known at compile time. This means one cannot resize these arrays dynamically as one can with heap-allocated arrays.
  • The stack is typically much smaller than the heap, so stack-allocated arrays are suitable for small to moderately sized arrays.
  • Stack-allocated arrays are automatically deallocated when they go out of scope.

Store Fixed-size Vectors and Strings on the Stack with arrayvec

arrayvec arrayvec~crates.io arrayvec~repo arrayvec~lib.rs cat~data-structures cat~no-std

A vector with fixed capacity, backed by an array (it can be stored on the stack too). Implements fixed capacity ArrayVec and ArrayString.

arrayvec↗ provides the types ArrayVec and ArrayString, which are stack-allocated, fixed size array-backed vector and string types.

ArrayVec is a vector-like collection with a fixed capacity that is determined at compile time. ArrayVec allocates its storage on the stack rather than on the heap, which can lead to better performance. It offers a simple API but also dereferences to a slice, so that the full slice API is available.

use arrayvec::ArrayVec;

/// This example demonstrates basic usage of `ArrayVec`.
///
/// `ArrayVec` is a vector backed by a fixed-size array.
///
/// The capacity is of type `usize` but is range-limited to `u32::MAX`.
/// It offers a simple API but also dereferences to a slice, so that the full
/// slice API is available.
fn main() {
    let mut array = ArrayVec::<_, 2>::new();
    assert_eq!(array.capacity(), 2);
    // Push some elements into the `ArrayVec`:
    array.push(1);
    array.push(2);
    assert!(array.is_full());
    // Trying to push beyond the capacity will result in a panic.
    // ERROR: array.push(3);
    let overflow = array.try_push(3);
    assert!(overflow.is_err());
    // Access elements:
    for i in 0..array.len() {
        println!("Element at index {}: {}", i, array[i]);
    }
    assert_eq!(&array[..], &[1, 2]);

    let mut array2: ArrayVec<i32, 3> = ArrayVec::from([1, 2, 3]);
    // Pop an element from the `ArrayVec`:
    if let Some(value) = array2.pop() {
        println!("Popped value: {value}");
    }
    assert_eq!(array2.len(), 2);
    assert!(!array2.is_empty());
}

Store Small Vectors on the Stack with Fallback to the Heap, with smallvec

smallvec smallvec~crates.io smallvec~repo smallvec~lib.rs cat~data-structures

smallvec↗ provides a vector that can store a small number of elements on the stack. Arrays that are stack-allocated will fallback to the heap if the fixed stack capacity is exceeded.

use smallvec::SmallVec;
use smallvec::smallvec;

/// This example demonstrates the usage of the `SmallVec` data structure from
/// the `smallvec` crate. `SmallVec` is a vector-like data structure that stores
/// elements inline when the number of elements is small, and switches to heap
/// allocation when the number of elements exceeds its inline capacity.
fn main() {
    // Create a SmallVec with a small inline capacity of 4.
    // This means that the first 4 elements will be stored directly within the
    // `SmallVec` struct, avoiding heap allocation.
    let mut small_vec: SmallVec<i32, 4> = SmallVec::new();

    // Push some elements into the SmallVec.
    small_vec.push(1);
    small_vec.push(2);
    small_vec.push(3);
    small_vec.push(4);

    // We can also initialize it via a macro:
    let mut small_vec: SmallVec<i32, 4> = smallvec![1, 2, 3, 4];

    // Print the current state of the SmallVec.
    println!("SmallVec (inline): {small_vec:?}");

    // Push beyond the inline capacity, causing a heap allocation.
    small_vec.push(5);

    // Print the state of the SmallVec after pushing beyond capacity.
    println!("SmallVec (heap-allocated): {small_vec:?}");

    // Access elements using indexing.
    for i in 0..small_vec.len() {
        println!("Element at index {i}: {}", small_vec[i]);
    }

    // Pop an element from the SmallVec.
    if let Some(value) = small_vec.pop() {
        println!("Popped value: {value}");
    }

    // Print the state of the SmallVec after popping.
    println!("SmallVec after popping: {small_vec:?}");

    // Split off the SmallVec.
    // `split_off` splits the vector into two at the given index.
    // The original vector will contain elements up to (but not including) the
    // index, and the new vector will contain the rest.
    let mut small_vec2 = small_vec.split_off(1);
    assert_eq!(small_vec, [1]);
    assert_eq!(small_vec2, [2, 3, 4]);

    // SmallVec points to a slice, so we can use normal slice indexing and
    // other methods to access its contents.
    small_vec2[0] = small_vec2[1] + small_vec2[2];
    small_vec2.sort();
}

Store Small Vectors on the Stack with Fallback to the Heap, with tinyvec

tinyvec tinyvec~crates.io tinyvec~repo tinyvec~lib.rs cat~no-std cat~data-structures

The tinyvec↗ crate provides a way to work with vectors that can store a small number of elements inline, without heap allocation, and dynamically grow to the heap if necessary. It is in 100% safe Rust code.

tinyvec is similar to smallvec↗ but with a smaller feature set and no dependencies.

Note that tinyvec↗ requires items to implement the Default↗ trait.

use tinyvec::TinyVec;

/// Demonstrates the usage of `TinyVec`, a vector-like data structure that
/// can store elements inline up to a certain capacity, and then falls back
/// to heap allocation for larger sizes.
fn main() {
    // Create a TinyVec with an inline capacity of 4 i32 elements.
    let mut tiny_vec: TinyVec<[i32; 4]> = TinyVec::new();

    // Push some elements into the TinyVec.
    tiny_vec.push(1);
    tiny_vec.push(2);
    tiny_vec.push(3);
    tiny_vec.push(4);

    // Print the current state of the `TinyVec`:
    println!("TinyVec (inline): {tiny_vec:?}");

    // Push beyond the inline capacity, which will cause a heap allocation.
    tiny_vec.push(5);

    // Print the state of the TinyVec after pushing beyond capacity.
    println!("TinyVec (heap-allocated): {tiny_vec:?}");

    // Access elements using indexing.
    for i in 0..tiny_vec.len() {
        println!("Element at index {i}: {}", tiny_vec[i]);
    }

    // Pop an element from the TinyVec.
    if let Some(value) = tiny_vec.pop() {
        println!("Popped value: {value}");
    }

    // Print the state of the `TinyVec` after popping:
    println!("TinyVec after popping: {tiny_vec:?}");
}
  • Heapless data structures.
  • Vectors.