# indextree: a Rust tree built on indices instead of reference counts

> indextree stores a whole tree in one vector and addresses the nodes by index, so there is no reference counting, no interior mutability, and a tree that can be sent or shared across threads like any other vector. The readme is unusually explicit about the trade, and the feature matrix tells you which parts of the standard library you give up for that.

**saschagrunert/indextree** — Arena based tree 🌲 structure by using indices instead of reference counted pointers

- Repository: https://github.com/saschagrunert/indextree
- Stars: 819 · Forks: 61
- Language: Rust
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/saschagrunert-indextree

## One vector, and numbers instead of pointers

indextree is described as an arena based tree structure that uses indices instead of reference counted pointers, and the readme explains the mechanism in four sentences.

The tree is using just a single vector and numerical identifiers, which are the indices in that vector, instead of reference counted pointers. There is no interior mutability cell, and mutability is handled in a way that is much more idiomatic to Rust through unique access to the arena. And because the structure is a vector of numbers rather than a graph of owned boxes, the tree can be sent or shared across threads like a vector, which enables general multiprocessing support such as parallel tree traversals.

That last sentence is the reason to care. A tree built from reference counted nodes is hard to share, because sharing a node means sharing its ownership, and a tree with parent pointers and child pointers has a cycle in it that a reference count can never finish collecting. The index model sidesteps the whole problem: the arena owns the nodes, the indices are copyable integers, and the parent and child relationships are stored as integers too.

The readme's example makes the shape concrete. You create an arena, add nodes to it, append one node to another, and then ask a node for its ancestors and count them. The arena is the only thing with a lifetime, and the nodes are handles into it.

## The feature matrix is the honest description of the crate

The features table is short, and it tells you what the crate is and what it costs.

Standard library support is on by default, and disabling it gives you a no_std build that requires only the allocator. The macros feature is also on by default and provides a macro for declarative tree construction. Serialisation support through serde is off by default, and parallel iteration is off by default and brings in rayon.

The pattern is worth reading as a design statement. The defaults are the ones most users want, and the optional ones are the ones that either add a dependency, like serde or rayon, or restrict the environment, like the no_std build.

There is one more line in that section that says more about maintenance than about features. The legacy feature name for serde is still accepted as an alias, which means a project that has been enabling the old name for years keeps working after an upgrade. That is the kind of detail that only appears in a crate that has been renamed its features at least once and has users, and it is a signal about how the maintainer treats compatibility.

The minimum supported Rust version is stated as 1.85.0, and for a crate that is in its fourth major series with releases in August and October 2026, that is a sensible floor rather than a moving target.

## The tree! macro builds a tree without a hundred lines of appends

The macro feature exists because building a tree by appending nodes one at a time is tedious, and the readme shows the declarative form.

```rust
use indextree::{Arena, macros::tree};

let arena = &mut Arena::new();
let root = tree!(arena, "root" => {
    "child_1" => {
        "grandchild_1",
        "grandchild_2",
    },
    "child_2",
    "child_3",
});

assert_eq!(root.child_count(arena), 3);
assert_eq!(root.descendants(arena).count(), 6);
```

The syntax is a nested block, with a comma separating siblings, and the macro returns the index of the root it built. The two assertions at the end are the kind of thing you want in a README, because they show both readings of the tree: the direct children of the root, and every node underneath it.

The macro is not in the main crate. The repository has a separate directory for the macro crate, and the workspace lists it as its own member, so the procedural macro is compiled independently and the main crate depends on it only when the feature is enabled. That is the standard arrangement for a crate with a proc macro, and it is also why the macro feature can be turned off without dragging a compiler dependency into a no_std build.

The other example in the readme is the one that probably matters more in practice, and it is a file system tree: a root, an etc and a usr directory, files under them, and then a traversal that collects the paths into a vector and compares them with the expected order. That is the shape of a real use, a program walking a hierarchy and collecting values in order.

## Parallel iteration is a feature flag away

The parallel example is the one that shows what the arena model buys you, and it is short.

With the parallel iteration feature enabled, trees can be traversed in parallel using rayon. The example builds a thousand children under a root and then sums the values with a parallel iterator over the arena, asserting that the total is the sum of the first thousand integers.

For that to compile, the arena has to be shareable across threads, which is exactly the property the readme claims when it says the tree can be sent or shared across threads like a vector. A reference counted tree could not do this without wrapping every node in its own synchronisation, and the cost of that is the thing the index model avoids.

There is a design question hiding in the example, though, and it is worth raising for anyone considering this crate. The traversal is over the arena rather than over a node, and the sum is a reduction over the values. That is the pattern the model supports naturally, and it is a pattern where the tree structure is incidental. If your workload is a traversal that needs the parent chain, or one that mutates the tree while walking it, the ergonomics are different, and the readme does not claim otherwise.

So the honest framing is that the parallel support is real and the primitive is the arena, not the tree. For workloads that are a map over a large hierarchy, that is exactly right.

## no_std is a configuration, not a fork

The readme shows how to use the crate without the standard library, and the configuration is a dependency entry.

```toml
[dependencies]
indextree = { version = "4", default-features = false }
```

Turning the default features off and depending on the crate with the standard library support disabled gives you a build that works in a no_std environment, with the allocator as the only requirement.

That is a pleasant thing to be able to say about a data structure crate, because the alternative is usually a fork or a maintained copy of the same source with the standard library parts removed. Here it is a feature flag, which means the no_std build and the standard build are the same code.

The two things you give up by turning the default features off are obvious from the table, standard library support and the declarative macro, and the one thing you get back is the ability to use the tree in an embedded or freestanding context. For a crate whose whole selling point is that it is a plain data structure over a vector, that is a natural fit rather than a feature bolted on.

## A workspace with a macro crate, a deny list and a conventional toolchain

The repository is small and the layout explains the build.

The workspace has two members, the crate itself and the macro crate, and the resolver is set to version 3. The root carries a codecov configuration, a deny configuration for the supply chain checks, a Makefile, a formatting configuration, a separate formatting configuration for the Rust sources, an editor configuration, and the standard metadata files.

The deny configuration is the interesting one for a security-minded adopter, because it is the file that pins what the crate is allowed to depend on and under which licences. For a data structure with no dependencies at all, that file is short, and having it is still a good signal about how the maintainer works.

The rest is the ordinary Rust project furniture: a GitHub Actions workflow with a badge, a coverage badge, a dependency status badge, badges for the documentation and for the published crate, and a link to the generated API documentation.

The release history supports the impression of a steady project. Versions 4.9.0 and 4.9.1 landed within a day of each other in late August 2026, and 4.9.2 followed in early October, with the last push on 2026-10-16. A crate in its fourth major series with releases every few weeks is a crate that is being used, and the version scheme, a fourth major line for a data structure, suggests the API has been revisited more than once.

The one thing a reader has to supply is the arena, and the readme is clear that a node is only meaningful together with it, which is the trade the whole design is built on.

## Conclusion

Adopt indextree if you are building a tree in Rust and have been bitten by reference counting or by the borrow checker around a tree with interior mutability, since the arena model removes both problems and lets you traverse in parallel. Do not adopt it if you need the tree to own its nodes independently, because the whole point is that a node is meaningful only together with its arena. Before you commit, check the feature matrix, since the standard library support is on by default and turning it off is what makes the crate usable in a no_std environment, and note that the minimum supported Rust version is 1.85, which is the floor your project has to meet.

## FAQ

### What is the indextree crate?

It is an arena based tree structure for Rust that stores every node in a single vector and addresses the nodes by index rather than by reference counted pointers. There is no interior mutability cell, mutability goes through unique access to the arena, and the tree can be sent or shared across threads like a vector, which enables parallel traversals.

### Which features does indextree have and which are optional?

Standard library support and the declarative tree macro are enabled by default, while serialisation through serde and parallel iteration with rayon are off by default and can be switched on. The older feature name for serde is still accepted as an alias, and the crate also offers a no_std build with the default features disabled, which needs only the allocator.

### How do I build a tree with indextree?

Either by appending nodes to an arena one at a time, or declaratively with the tree macro, whose nested syntax mirrors the tree structure and which returns the index of the root. The readme shows a file system example that appends directories and files and then collects the paths by walking the descendants.

### Can indextree be traversed in parallel?

Yes, with the parallel iteration feature, which uses rayon. The readme's example sums a thousand children of a root with a parallel iterator over the arena, which works because the tree can be shared across threads in the same way as a vector.

### What is the minimum Rust version for indextree?

The readme states the current minimum supported Rust version as 1.85.0. The crate is in its fourth major series, with releases 4.9.0 and 4.9.1 in late August 2026 and 4.9.2 in early October, and the repository builds as a workspace of two members, the crate and its macro crate.

## Sources

- [Issues](https://github.com/saschagrunert/indextree/issues)
- [License: MIT](https://github.com/saschagrunert/indextree/blob/main/LICENSE)
- [README](https://github.com/saschagrunert/indextree/blob/main/README.md)
- [Releases](https://github.com/saschagrunert/indextree/releases)
- [saschagrunert/indextree on GitHub](https://github.com/saschagrunert/indextree)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/saschagrunert-indextree
