# Talc: a general-purpose allocator for no_std and WebAssembly builds

> Talc is a MIT-licensed Rust allocator aimed at embedded systems, kernels and WebAssembly modules, with a lock-free single-threaded cell, a lock-based multi-threaded variant, and support for many independently managed heaps. It suits a no_std or wasm target where the system allocator is not an option, and its own documentation says a hosted system should use jemalloc or mimalloc instead.

**SFBdragon/talc** — A fast and flexible allocator for no_std and WebAssembly

- Repository: https://github.com/SFBdragon/talc
- Website: https://crates.io/crates/talc
- Stars: 567 · Forks: 21
- Language: Rust
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/sfbdragon-talc

## The stated audience, and the honest case against it

Talc is a fast and flexible allocator for no_std and WebAssembly, and the project documentation is unusually direct about who it is for.

The what is this for list has two entries. Embedded systems, OS kernels and other no_std environments, and WebAssembly modules, where it is described as a drop-in replacement for DLmalloc. The second point is the more specific one: in a browser, the WebAssembly module needs an allocator, the obvious candidate is the one that ships inside the wasm standard library, and Talc exists to be a better-behaved replacement for it.

Then there is a section called Why not Talc, and its presence is a good sign. It says that if you are on a mature hosted system, especially one that jemalloc or mimalloc supports, you should consider those instead. They provide allocation concurrency, well-tested virtual memory API integration, and are more mature and sophisticated and more cleverly implemented than Talc. It even notes that the default allocator for Rust on Linux has impressive performance characteristics.

That is an allocator library telling you when not to use it, which is rarer than it should be. The reasoning is also technically correct: a general-purpose allocator for a hosted system does things that Talc does not attempt, notably integration with the operating system's virtual memory API, which is how a mature allocator gets large object support, background thread management and page-level control.

So the decision is a simple one. If your program runs on Linux with a normal Rust toolchain, use the system allocator. If your program is a kernel, a microcontroller image or a WebAssembly module, where there is no system allocator to use and no virtual memory to talk to, then a purpose-built allocator is the only option, and this is one.

The crate is published on crates.io as talc, with API documentation on docs.rs, and the licence is MIT.

## Two front ends over one allocator: TalcCell and TalcLock

The feature list separates two ways of reaching the allocator, and the distinction is the most important design decision in the crate.

TalcCell is the safe, lockless option for single-threaded allocation, and it works with both the GlobalAlloc trait and the newer Allocator trait. The word lockless is the operative one. In a single-threaded program a mutex is pure overhead, and even an uncontended one costs an atomic operation on every allocation, which matters when the allocations are small and frequent, which is exactly the pattern in a game engine or an embedded loop.

TalcLock is the multi-threaded variant, and it uses the lock_api crate for the locking rather than a mutex type of its own, which means the locking behaviour is configurable in the way lock_api is designed for. Using the standard lock_api abstraction rather than std::sync::Mutex directly is what lets the same allocator work in a no_std environment, where std's mutex is not available.

That connection to lock_api is the key to the crate's design. A no_std program has no standard library, so any synchronisation primitive has to come from a separate crate, and lock_api is the usual choice because it abstracts over spinlocks, semaphores and mutexes behind one interface. TalcLock therefore works in an environment where a std mutex cannot exist.

The crate also supports allocator-api2, which is what makes the Allocator trait usable in safe Rust, since the trait in the standard library is unsafe to implement directly and allocator-api2 provides a safe wrapper.

Underneath both front ends sits the same allocator, with a heap model that is the other substantial feature: it supports creating and resizing arbitrarily many heaps, manually or automatically, and supports automatic reclaim of unused memory. The heap model is the part that matters for embedded work, where the whole point is that you tell the allocator what region of memory it may use, and the part that matters for WebAssembly, where the module owns a linear memory that has to be handed back.

## The feature matrix the project actually compiles

The repository contains a justfile whose check recipe is the clearest statement of what the crate supports, because it enumerates the configurations rather than describing them.

The recipe starts with a set of stable configuration checks:

```bash
rustup run stable cargo check -p talc
rustup run stable cargo check -p talc --features=disable-grow-in-place
rustup run stable cargo check -p talc --features=counters
rustup run stable cargo check -p talc --target wasm32-unknown-unknown
```

The three feature flags are worth reading as a set. disable-grow-in-place and disable-realloc-in-place exist because growing a heap in place, by extending an existing allocation rather than by allocating a new one and copying, is an optimisation with a portability cost, and the flags let a target where that is unsafe turn it off. counters adds allocation statistics for debugging and performance insights. The recipe checks the flags individually and in combination, and then repeats the checks against the wasm32-unknown-unknown target, because the WebAssembly path is a different compilation and a feature combination that builds natively may not build for the browser.

Then it checks the examples, which are the three ways you would actually use the crate: an example named allocator_api, one named global_allocator and one named no_std_global_allocator. The difference between global_allocator and no_std_global_allocator is the distinction between a program with a standard library and one without, and having an example for each means the no_std path is exercised rather than assumed.

And finally the documentation is tested, with cargo test -p talc --doc and cargo test -p doctest --doc, so the code in the doc comments compiles.

The workspace layout backs this up. The Cargo workspace has default-members set to talc, so a bare cargo command in the repository root builds the crate rather than the benchmark and fuzzing members, and the members are benches, fuzz, talc, wasm-perf, wasm-size and doctest. The dev and release profiles both set panic to abort, with a comment noting that the bench and test profiles unwind automatically, which is the usual arrangement for a crate that wants small binaries.

## How the project measures itself, and what the numbers are for

The repository has a BENCHMARKS.md for Linux on x86_64 and a BENCHMARKS_WASM.md for the WebAssembly case, and that split reflects the two audiences rather than an accident of history.

The benchmark infrastructure is visible in the tree and in the justfile. There is a benches/ member for the native benchmarks and a wasm-perf/ member for the WebAssembly performance measurement, plus a wasm-size/ member that tracks the size of the compiled module, which is a separate concern from its speed. The Cargo profiles for both wasm-size and wasm-perf set opt-level to z and codegen-units to 1, with a comment asking to be realistic about the optimisation configuration even when it is a benchmark, which is a good practice: a benchmark built with a different profile than the release build measures something nobody ships.

The justfile collects the results into CSV files under results/, with separate files for heap efficiency, memory efficiency, microbench and wasm size and wasm performance, and then renders plots from them into a plots/ directory as PNG files. Storing the raw numbers in version control alongside the plots means a change in the allocator can be compared against the previous run rather than against a screenshot.

The heap efficiency and memory efficiency measurements are the interesting ones for the crate's stated use. On a hosted system an allocator that wastes memory is a problem you can measure with a profiler. On an embedded target with a fixed 64 KB heap, the fraction of that heap you can actually use is the difference between a program that fits and one that does not, so measuring it in the same repository as the implementation is the difference between a guess and a number.

Correctness is covered separately. The feature list states that correctness is verified with tests, MIRI and fuzzing, and the repository has a fuzz/ member with its own release profile carrying debug info because the fuzzer needs it, which is a detail that tells you the fuzzing setup is a real one rather than a stub.

## The trade-off: a smaller, less capable allocator

The honest way to describe Talc is that it gives up the features a mature general-purpose allocator has in exchange for being usable where those features cannot exist.

On a hosted system, jemalloc and mimalloc exist because allocation is a real cost and the operating system offers tools to manage it. They integrate with virtual memory, so a large allocation is backed by fresh pages rather than by carving a heap, they run background threads that return memory to the operating system when it is idle, and they have per-thread caches that make the common case a bump pointer. Talc does none of that, and the project says so.

What Talc offers instead is control over where the memory comes from, which is what an embedded or WebAssembly program needs. A program in a wasm module does not have an operating system to ask; it has a linear memory, and the allocator has to work within it. A kernel has a physical page frame allocator and needs to hand out regions. Talc's heap model, with many heaps created and resized manually or automatically and with automatic reclaim of unused memory, is designed for exactly that division of labour: the program decides the boundaries and the allocator manages the blocks inside them.

The counters feature fits the same pattern. On a hosted system, allocation statistics are a diagnostic you turn on when something is slow. In an embedded system, the same statistics are how you find out that your heap is 30 percent wasted in blocks of the wrong size, which is a design problem rather than a tuning problem.

The consequence for a user is that Talc is a specialist tool with a narrow fit, and the project would rather you knew that than sold you a jemalloc replacement. The performance claims are backed by the two benchmark documents, but the numbers in them are measurements of a specific allocator against specific alternatives, and the WebAssembly case and the embedded case are different workloads with different constraints.

The maintenance picture is straightforward. The only listed release is v4.4.2 from 2024-10-27, and the last push to the repository was on 2026-09-09, so the branch is being worked on even though the version line has not moved since 2024. The author asks for donations through PayPal or Ko-fi, which is normal for a crate that a lot of embedded projects quietly depend on.

## Conclusion

Talc is worth evaluating for a no_std target or a WebAssembly module, because the same crate serves both with a drop-in replacement for DLmalloc in the browser case, because the single-threaded path is lock-free rather than a mutex with an uncontended fast path, and because the heap management lets you reclaim regions that a general-purpose allocator would keep. It is the wrong tool for a hosted Linux service, and the project says so itself. Before you adopt it, check three things: that you actually need a custom allocator, since on a mature hosted system the documentation recommends jemalloc or mimalloc for their allocation concurrency and virtual memory integration, and even Rust's default allocator on Linux has impressive performance; that the feature flags you need exist in the combination you want, because the check recipe in the repository tests the crate across disable-grow-in-place, disable-realloc-in-place and counters in several combinations, and a configuration outside that matrix is one nobody has compiled; and that you pin the version, since the only listed release is v4.4.2 from 2024-10-27 while the last push to the repository was on 2026-09-09.

## FAQ

### What is Talc and what is it used for?

Talc is a dynamic memory allocator written in Rust for no_std environments and WebAssembly. The project states its use as embedded systems, OS kernels and other no_std environments, and WebAssembly modules where it works as a drop-in replacement for DLmalloc. It also says plainly that on a mature hosted system you should use jemalloc or mimalloc instead.

### What is the difference between TalcCell and TalcLock?

TalcCell is the safe, lockless option for single-threaded allocation and works with both the GlobalAlloc and Allocator traits, while TalcLock is the multi-threaded variant and uses the lock_api crate for its locking. Using lock_api rather than a standard library mutex is what allows the same allocator to work in a no_std environment where the standard library is unavailable.

### Which Rust feature flags does the Talc crate support?

The crate supports disable-grow-in-place and disable-realloc-in-place for targets where in-place growth is not safe, and counters for allocation statistics used in debugging and performance analysis. The repository's check recipe compiles the crate with those flags individually and combined, and repeats the checks against the wasm32-unknown-unknown target.

## Sources

- [License: MIT](https://github.com/SFBdragon/talc/blob/master/LICENSE)
- [Project website](https://crates.io/crates/talc)
- [README](https://github.com/SFBdragon/talc/blob/master/README.md)
- [Releases](https://github.com/SFBdragon/talc/releases)
- [SFBdragon/talc on GitHub](https://github.com/SFBdragon/talc)

---

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