rust-obstack ============ A fast segmented stack allocator, supporting multiple objects of any type. A type of arena allocator, obstacks deallocate memory all at once, when the `Obstack` itself is destroyed. The benefit is extremely fast allocation: just a pointer bump. Unlike a typed arena, a single `Obstack` can contain values with any number of different types. For `Copy` types pushing a value to an `Obstack` returns a standard mutable reference: let r: &mut u8 = stack.push_copy(42); assert_eq!(*r, 42); As `Copy` types can't implement `Drop`, nothing needs to be done to deallocate the value beyond deallocating the memory itself, which is done en-mass when the `Obstack` itself is dropped. `push_copy()` is thus limited to types that implement `Copy`. Types that do not implement `Copy` *may* implement `Drop`. As Rust's type system doesn't have a negative `!Drop` trait, `Obstack` has a second method - `push()` - that is not restricted to `Copy` types. This method returns the wrapper type `Ref`, that wraps the underlying mutable reference. This wrapper owns the value on the stack, and ensures that `drop` is called when the wrapper goes out of scope. Essentially `Ref` is the equivalent of `Box`, but using an `Obstack` rather than the heap. In practice even when the `Ref` wrapper is used, if the underlying type doesn't actually implement a meaningful `drop` method, the Rust compiler is able to optimize away all calls to `drop`, resulting in the same performance as for `Copy` types; the actual `Ref::drop()` method is `[inline(always)]` to aid this process. This is important as not all non-drop types can implement `Copy` - notably mutable references can't. `Obstack` allocates memory as a segmented stack consisting of one or more segments of contiguous memory. Each time the top segment becomes full, a new segment is allocated from the heap. To ensure the total number of allocations remains small, segments are allocated in a powers-of-two fashion, with each segment being twice the size of the previous one. Once a segment has been allocated it's stable for the life of the `Obstack`, allowing the values contained in that segment to be referenced directly; Rust lifetimes ensure that the references are valid for the lifetime of the `Obstack`. ## Benchmarks Some preliminary benchmarks can be run with: cargo bench tl;dr: Approximately 10x faster to allocate and deallocate a linked-list.