scattered-collect 0.22.0

Link-time collections for Rust (distributed slices, registries)
Documentation

Scattered Collections

Build Status

The crate is part of the linktime project.

crate
linktimedocs.rs crates.io Convenience crate for ctor, dtor and link-section
ctordocs.rs crates.io Module initialization functions before main
dtordocs.rs crates.io Module shutdown functions before main
link-sectiondocs.rs crates.io Linker-managed typed (slices) and untyped sections
scattered-collectdocs.rs crates.io Linker-managed collections: slices, sorted slices, maps

A crate for defining linker-managed scattered collections in Rust.

The collections come in a 'referenced' and 'unreferenced' variant. The referenced variants allow you to access the items as static handles at the declaration site, while the unreferenced variants allow you to access the items as a slice only. The latter, unreferenced variants may be more efficient.

MSRV

This crate currently has a MSRV of Rust >= 1.85.

For collections that have duplicate names across different files, Rust >= 1.88 is required (the proc_macro_span is used to generate unique filenames).

Zero-allocation collections

The collections are all zero-allocation. This means that they can be used in no-std/no-alloc environments, and that they do not contribute to heap usage whatsoever.

Free ID generation

Each item is placed in a section which allows for free identifier generation. Use each collection's offset_of method to get the offset of an item in the collection. This ID is guaranteed to be stable per executable build.

Collections

  • [ScatteredIterable]: A collection of items that are available only via iterator.
  • [ScatteredSlice]: A collection of sized items that collected into a slice in an arbitrary order.
  • [ScatteredSortedSlice]: A collection of items that are available via slice, in sorted order.
  • [ScatteredReferencedSlice]: A collection of items collected into a slice (link order), with each static item auto-wrapped as [referenced_slice::Ref].
  • [ScatteredSortedReferencedSlice]: A collection of sized items that are available both via sorted slice and via reference at the declaration site (auto-wrapped as [sorted_referenced_slice::Ref]).
  • [ScatteredMap]: A collection of key-value pairs that are available via slice, as well as indexed by key.
Collection Ordering Indexed Access Unique Keys Per-Item Handles Notes
ScatteredSlice Arbitrary (link order) Yes (slice) No † No Basic un-ordered slice
ScatteredSortedSlice Sorted Yes (slice) No † No Sorted slice
ScatteredIterable Arbitrary (link order) No (iterator only) No † Yes Singly-linked list
ScatteredReferencedSlice Arbitrary (link order) Yes (slice) No † Yes Un-ordered slice with handles
ScatteredSortedReferencedSlice Sorted Yes (slice) No † Yes Sorted slice with handles
ScatteredMap None (link order) Yes (by key or entry) Yes Yes Swiss-table style map
ScatteredSet None (link order) n/a Yes No Swiss-table style set

† Each item in the collection is assigned a unique ID which is guaranteed to be stable per executable build.

Re-exporting the scatter/gather macros

If you wrap these collections in your own library so that downstream users don't need to depend on scattered-collect directly, you have two options.

The (preferred) option is to re-export the [declarative] forms of the macros. They resolve their support paths back to scattered-collect through the re-exporting crate, so no extra configuration is needed:

// In your library crate:
pub use scattered_collect::declarative::{gather, scatter};
pub use scattered_collect::slice::ScatteredSlice;

Downstream users then invoke them as gather! { #[gather] ... } and scatter! { #[scatter(COLLECTION)] ... }.

Alternatively, the proc-macro #[scatter] / #[gather] attribute forms emit a fixed ::scattered_collect path and so require a direct dependency by default. Pass crate_path = <path> to redirect them to wherever scattered-collect has been re-exported:

# use scattered_collect::{gather, scatter, slice::ScatteredSlice};
# fn main() {}
#[gather(crate_path = ::scattered_collect)]
static ITEMS: ScatteredSlice<u32>;

#[scatter(crate_path = ::scattered_collect, ITEMS)]
const _: u32 = 1;

Scatter/Gather syntax

The collections are defined as a single scatter call with multiple gather calls that submit items to the collection.

# use scattered_collect::{gather, scatter, slice::ScatteredSlice};
# struct DatabaseDriver(&'static str);
#[gather]
static COLLECTION: ScatteredSlice<DatabaseDriver>;

... and then elsewhere in your crate:

# mod root { // doctests have weird scope for macros...
# use scattered_collect::{gather, scatter, slice::ScatteredSlice};
# struct DatabaseDriver(&'static str);
# #[gather] static COLLECTION: ScatteredSlice<DatabaseDriver>;

mod postgres {
    # use super::*;
    #[scatter(COLLECTION)]
    static POSTGRES_DRIVER: DatabaseDriver = DatabaseDriver("postgres" /*, ...*/);
}

mod mysql {
    # use super::*;
    # struct DatabaseDriver(&'static str);
    #[scatter(COLLECTION)]
    static MYSQL_DRIVER: DatabaseDriver = DatabaseDriver("mysql" /*, ...*/);
}
# }