`realloc`, my stab at the allocation API

I've been working on a project to re-implement std (restd) for a little while, and recently I decided to write my own version of an allocator API from scratch. It's cross-platform and no_std (unless you enable the platform allocators).

It's focused on two main features: safety and genericity. It does sacrifice some performance for these design goals.

  • The public API is totally safe (the unsafe parts are delegated to the allocators themselves)
  • Each container type is generic over allocator and how to handle allocation failure
    • The global allocator can also be swapped out at-will

At the moment, it's mostly a minimum viable product: the bones are all there and documented, and there are system allocators for Linux and Windows; but there are only two "special" allocators (Arena and Array) and only two container types (Box and Vec). However, I'm still mostly happy with how it's turned out so far.

Now that it's mostly done, I'm curious what others think of it. Having run cargo miri on some examples, and with the small number of unsafe code blocks to verify, I'm fairly certain it's sound (except I've made none of those "size is less than isize::MAX bytes" guarantees yet). Here's a small code example:

// you *could* just use the platform allocator... but that's no fun!
use realloc::{Arena, Array, Static, Vec, Box, Fallible, Panic};

// a constant-size array allocator
static BASE: Static<Array<1024 * 1024>> = Static::new(|| Array::new());
// an arena allocator layered on the previous one
static ARENA: Static<Arena<'_>> = Static::new(|| Arena::new(&BASE));

fn main() {
    let mut x = Vec::<i32, Fallible>::new_in(&ARENA);

    // notice how these return `Result<(), Error>`...
    x.push(1).expect("failed to allocate for x!");
    x.push(2).expect("failed to allocate for x!");
    x.push(3).expect("failed to allocate for x!");
    assert_eq!(&*x, [1, 2, 3]);

    x.clear();
    let mut x = x.change_strategy::<Panic>();

    /// ...while these just return `()`
    x.push(4);
    x.push(5);
    x.push(6);
    assert_eq!(&*x, [4, 5, 6]);

    // and if you don't like specifying your allocator all the time...
    realloc::global::set(&ARENA);

    // ...use the global allocator instead
    let y = Box::<i32, Panic>::new(x.pop().unwrap());
    assert_eq!(&*y, 6);
}

All kinds of feedback are welcome, but I'm mostly interested in conceptual/API feedback (I know how long it takes to give a full code review, I'm not expecting that).

https://crates.io/crates/realloc

1 Like