Cloud Parts

immer

A header-only C++ library of persistent, immutable containers, including an RRB-tree flex_vector with O(log n) concatenation and slicing and policy-based memory management.

What it is
Purely functional data structures (rrb vectors)
Type
Open source
Made by
Juan Pedro Bolívar Puente
License
Boost Software License 1.0
Facts checked
9 October 2026

What it is

immer brings persistent data structures to C++ with an API that feels like the standard library. vector is a radix tree that stores elements in contiguous chunks (32 per chunk for pointer-sized types); flex_vector is an RRB tree that adds O(log n) concatenation, slicing and insertion anywhere, at a constant-factor cost of 1 to 2 times on other operations once those are used. array is a plain copy-on-write array, map, set and table are hash tries, and box wraps a single immutable value. Every method that would change a container is const and returns a new one.

The library is built for systems code without a managed runtime. Memory management is a template policy: by default immer uses thread-safe reference counting and a free-list heap, and it can plug into a tracing garbage collector instead, so it can sit inside the runtime of languages like Guile or Python. Elements are stored unboxed in the tree nodes. Transients and move semantics let code apply a batch of updates in place when a container isn’t shared, then freeze the result with persistent().

Juan Pedro Bolívar Puente described the design in “Persistence for the Masses: RRB-Vectors in a Systems Language” (ICFP 2017) and a CppCon 2017 talk, and built the demo text editor Ewig on it; Bolívar Puente’s Redux-like library Lager follows the same value-oriented style. The project is maintained: 0.9.0 came out in December 2025 with experimental immer::persist serialization, and 0.9.1 in January 2026.

Why teams pick it

  • Value semantics for big data: copying a container copies a pointer, and comparing old and new versions is cheap
  • flex_vector concatenates and slices in O(log n), useful for editors and for splitting and joining parallel work
  • Header-only with no dependencies, packaged for vcpkg and build2
  • Pluggable memory policies: reference counting by default, or a tracing garbage collector when embedded in another language runtime

Watch out for

  • Still pre-1.0, with sparse releases: 0.8.1 in 2023, then 0.9.0 in December 2025
  • Reading is slower than std::vector: every access walks a few tree levels instead of indexing one array
  • Large element types make the contiguous chunks big and slow; the docs suggest wrapping them in immer::box. The default reference counting is thread-safe, so every copy pays for atomic operations unless you pick another policy

Alternatives

What all rrb vectors have in common →

How it fits with other technologies

Build with a transient, update persistently, concatenate and slice (immer 0.9.1, C++17) · cpp

#include <immer/flex_vector.hpp>
#include <immer/flex_vector_transient.hpp>
#include <immer/map.hpp>
#include <iostream>
#include <string>

int main() {
    // build a big vector through a transient, then freeze it
    auto t = immer::flex_vector<int>{}.transient();
    for (int i = 0; i < 100000; ++i) t.push_back(i);
    const auto v1 = t.persistent();

    // every "change" returns a new vector; v1 is untouched
    const auto v2 = v1.set(0, 42);
    std::cout << v1[0] << " " << v2[0] << "\n"; // 0 42

    // flex_vector is an RRB tree: O(log n) concatenation and slicing
    const auto joined = v1 + v2;
    const auto middle = joined.drop(99990).take(20);
    std::cout << joined.size() << " " << middle.size() << " "
              << middle[10] << "\n"; // 200000 20 42

    const auto m1 = immer::map<std::string, int>{}.set("tea", 3);
    const auto m2 = m1.set("cake", 5);
    std::cout << m1.size() << " " << m2.size() << "\n"; // 1 2
}

See it in a real stack

No architecture uses immer yet. Drop it into the playground and wire it up yourself.Open playground →
Where it came from2016–2026

Clojure, Scala and JavaScript libraries such as Immutable.js had made persistent collections common in garbage-collected languages, especially for interactive apps built around React. Juan Pedro Bolívar Puente wanted the same for C++, where there is no garbage collector, values are often stored unboxed for cache locality, and developers sometimes need to escape immutability for a batch of updates. Bolívar Puente started immer on GitHub in August 2016.

Bolívar Puente’s ICFP 2017 paper set out the answers: an embedding RRB tree that stores unboxed elements, tree algorithms written independently of node-layout optimisations, policy-based memory management covering both reference counting and tracing garbage collection, and transients based on reference counts and C++ move semantics, including for concatenation. Bolívar Puente presented it to C++ developers at CppCon 2017 as “Postmodern Immutable Data Structures”. Releases have been infrequent: 0.5.0 in 2018, 0.6 in 2019, 0.7.0 in 2021, 0.8 in 2022 and 2023, and 0.9 in December 2025.

Why it’s built this way

RRB trees rather than plain radix vectors
Relaxed nodes add O(log n) concatenation and slicing while keeping lookups and updates effectively constant, as in Clojure and Scala vectors.
Policy-based memory management
Without a runtime garbage collector, efficient persistence needs reference counting; but when embedded in a language that has a collector, immer should use that instead. A template policy covers both.
Unboxed elements in the nodes
Most immutable collections store pointers to boxed objects; performance-critical C++ code needs values inline for cache locality.
Transients through reference counts and move semantics
If a container is the only owner of its nodes, or is an r-value about to be discarded, it can be updated in place, so functional-style code gets close to mutable performance.

Timeline

  1. 2016

    Repository created on GitHub (August)

  2. 2017

    ICFP paper “Persistence for the Masses” and CppCon talk

  3. 2018

    Version 0.5.0

  4. 2022

    Version 0.8.0

  5. 2025

    Version 0.9.0 adds experimental immer::persist

  6. 2026

    Version 0.9.1 (January)

Sources6 · facts checked 9 October 2026

Improve this page on GitHub