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.