#nanbox #tagged-pointers #pointers #tagged-enum

no-std tagged-box

An implementation of Box that uses tagged pointers

2 releases

0.1.1 Mar 8, 2020
0.1.0 Mar 8, 2020

#881 in Embedded development

Download history 18/week @ 2022-11-28 36/week @ 2022-12-05 42/week @ 2022-12-12 39/week @ 2022-12-19 41/week @ 2022-12-26 11/week @ 2023-01-02 29/week @ 2023-01-09 20/week @ 2023-01-16 53/week @ 2023-01-23 69/week @ 2023-01-30 25/week @ 2023-02-06 41/week @ 2023-02-13 106/week @ 2023-02-20 57/week @ 2023-02-27 45/week @ 2023-03-06 31/week @ 2023-03-13

255 downloads per month
Used in 11 crates (via litl)


964 lines


Crates.io Docs.rs GitHub LOC

A no_std, zero-dependency crate for the creation and management of NaN-boxed types with Box-like semantics and tagged pointers, tagged pointers and a macro interface to safely create NaN-boxed enums.


First, add the crate to your Cargo.lock (Note: for variable reserved widths, see the features section)

tagged_box = "0.1.0"

Next, for using the macro, add the following to the top of your file

use tagged_box::{tagged_box, TaggableContainer, TaggableInner};

Then you can use the macro as follows

tagged_box! {
    #[derive(Debug, Clone, PartialEq)]
    struct Container, enum Item {

let container = Container::from(String::from("Hello from tagged-box!"));

    Item::String(String::from("Hello from tagged-box!"))

For working with NaN-boxes, simply add

use tagged_box::TaggedBox;

And for tagged pointers use

use tagged_box::TaggedPointer;

What this crate does

This crate implements NaN-Boxing and Tagged Pointers, which are a way to store extra data in the unused bits of pointers. While the two differ in implementation, they are semantically the same. In this crate, the TaggedBox type allows you to store anywhere from 7 to 16 bits of arbitrary data in your pointer, depending on the features enabled. For explanation's sake, I'll be using the 48bits feature to explain, as it's the default and leads to the cleanest examples.
The pointers this applies to are 64 bits long, looking something like this

0000 0000 0000 0000

However, not all of these bits are used for the actual addressing of memory, so most pointers look like this

Free Data!

Those first 16 bits are free data, just begging to be used, and that's what TaggedPointer does. TaggedPointer simply manages the pointer and the data (referred to as a 'discriminant' throughout this crate), making sure you get a pointer when you need it and data when you need it, and not mixing those two up.

TaggedBox goes one layer higher, storing an enum discriminant (Indicated by the type parameter) and directly storing the enum variant's inner value to the heap. In short, TaggedBox is like a Box and an enum rolled into one.

Ramping the abstraction up one more notch, we have the tagged_box! macro, which creates a container-type struct and an associated TaggedBox-backed enum that can be seamlessly transferred between.

Cargo Features

This crate has a few features that change the number of free and reserved bits:

  • 48bits (On by default): 48 bits of reserved pointer, 16 bits for data
  • 49bits: 49 bits of reserved pointer, 15 bits for data
  • 50bits: 50 bits of reserved pointer, 14 bits for data
  • 51bits: 51 bits of reserved pointer, 13 bits for data
  • 52bits: 52 bits of reserved pointer, 12 bits for data
  • 53bits: 53 bits of reserved pointer, 11 bits for data
  • 54bits: 54 bits of reserved pointer, 10 bits for data
  • 55bits: 55 bits of reserved pointer, 9 bits for data
  • 56bits: 56 bits of reserved pointer, 8 bits for data
  • 57bits: 57 bits of reserved pointer, 7 bits for data

However, only one of these may be active at a time, otherwise a compile_error will be emitted.

To select a feature, put the following in your Cargo.toml

version = "0.1.0"
default-features = false
features = ["50bits"] # Select your feature here

No runtime deps


  • 48bits
  • 49bits
  • 50bits
  • 51bits
  • 52bits
  • 53bits
  • 54bits
  • 55bits
  • 56bits
  • 57bits