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_vectorconcatenates 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
What immer uses
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
- 2016
Repository created on GitHub (August)
- 2017
ICFP paper “Persistence for the Masses” and CppCon talk
- 2018
Version 0.5.0
- 2022
Version 0.8.0
- 2025
Version 0.9.0 adds experimental
immer::persist - 2026
Version 0.9.1 (January)
Sources6 · facts checked 9 October 2026
- immer documentation: Containers (box, array, vector, flex_vector, set, map, table)
- immer documentation: Memory management (default policy, reference counting, garbage collection)
- immer source: immer/detail/hamts/champ.hpp (map and set as hash tries)
- immer README (header-only, C++14, vcpkg and build2, Boost licence)
- Juan Pedro Bolívar Puente: Persistence for the Masses: RRB-Vectors in a Systems Language (ICFP 2017)
- immer releases on GitHub (v0.9.1, January 2026)