Expand description

Sized Chunks

This crate contains three fixed size low level array like data structures, primarily intended for use in immutable.rs, but fully supported as a standalone crate.

Their sizing information is encoded in the type using const generics. For example, SparseChunk<A, 32> would give you a sparse array with room for 32 elements of type A. You can also omit the size, as they all default to a size of 64, so SparseChunk<A> would be a sparse array with a capacity of 64.

All data structures always allocate the same amount of space, as determined by their capacity, regardless of how many elements they contain, and when they run out of space, they will panic.

Data Structures

TypeDescriptionPushPopDeref to &[A]
ChunkContiguous arrayO(1)/O(n)O(1)Yes
RingBufferNon-contiguous arrayO(1)O(1)No
SparseChunkSparse arrayN/AN/ANo

The Chunk and RingBuffer are very similar in practice, in that they both work like a plain array, except that you can push to either end with some expectation of performance. The difference is that RingBuffer always allows you to do this in constant time, but in order to give that guarantee, it doesn’t lay out its elements contiguously in memory, which means that you can’t dereference it to a slice &[A].

Chunk, on the other hand, will shift its contents around when necessary to accommodate a push to a full side, but is able to guarantee a contiguous memory layout in this way, so it can always be dereferenced into a slice. Performance wise, repeated pushes to the same side will always run in constant time, but a push to one side followed by a push to the other side will cause the latter to run in linear time if there’s no room (which there would only be if you’ve popped from that side).

To choose between them, you can use the following rules:

  • I only ever want to push to the back: you don’t need this crate, try ArrayVec.
  • I need to push to either side but probably not both on the same array: use Chunk.
  • I need to push to both sides and I don’t need slices: use RingBuffer.
  • I need to push to both sides but I do need slices: use Chunk.

Finally, SparseChunk is a more efficient version of Vec<Option<A>>: each index is either inhabited or not, but instead of using the Option discriminant to decide which is which, it uses a compact bitmap. You can also think of SparseChunk<A, N> as a BTreeMap<usize, A> where the usize must be less than N, but without the performance overhead. Its API is also more consistent with a map than an array - there’s no push, pop, append, etc, just insert, remove and lookup.

InlineArray

Finally, there’s InlineArray, which is a simple vector that’s sized to fit inside any Sized type that’s big enough to hold a size counter and at least one instance of the array element type. This can be a useful optimisation when implementing a list like data structure with a nontrivial set of pointers in its full form, where you could plausibly fit several elements inside the space allocated for the pointers. imbl::Vector is a good example of that, and the use case for which InlineArray was implemented.

Feature Flags

The following feature flags are available:

FeatureDescription
arbitraryProvides Arbitrary implementations from the arbitrary crate. Requires the std flag.
refpoolProvides PoolDefault and PoolClone implemetations from the refpool crate.
ringbufferEnables the RingBuffer data structure.
stdWithout this flag (enabled by default), the crate will be no_std, and absent traits relating to std::collections and std::io.

Re-exports

Modules