#[array_slots]Expand description
Generate slot index constants, a borrowed view struct, and a typed ext trait from a slot struct definition.
Fields must be ArrayRef (required slot), Option<ArrayRef> (optional slot), or
Vec<ArrayRef> (variadic tail of required slots).
Every field must carry a #[slot(..)] attribute naming the exact slot index it maps
to. The attribute — not the declaration order — defines the storage layout, so fields
may be reordered, grouped, or documented in any order without changing the slot
indices an array is built from or read back with.
§Example
#[array_slots(Patched)]
pub struct PatchedSlots {
#[slot(0)]
pub inner: ArrayRef,
#[slot(1)]
pub lane_offsets: ArrayRef,
#[slot(2)]
pub patch_indices: ArrayRef,
#[slot(3)]
pub patch_values: ArrayRef,
}§Generated output
Given the above, the macro generates:
// --- The original struct, minus the consumed `#[slot(..)]` attributes ---
pub struct PatchedSlots { ... }
// --- Slot index constants and conversion methods on the struct ---
impl PatchedSlots {
pub const INNER: usize = 0;
pub const LANE_OFFSETS: usize = 1;
pub const PATCH_INDICES: usize = 2;
pub const PATCH_VALUES: usize = 3;
pub const COUNT: usize = 4;
pub const NAMES: [&'static str; 4] = ["inner", "lane_offsets", "patch_indices", "patch_values"];
/// Take ownership of slots from an `ArraySlots`.
pub fn from_slots(slots: ArraySlots) -> Self { ... }
/// Convert back into storage order.
pub fn into_slots(self) -> ArraySlots { ... }
}
// --- Borrowed view with &ArrayRef / Option<&ArrayRef> fields ---
pub struct PatchedSlotsView<'a> {
pub inner: &'a ArrayRef,
pub lane_offsets: &'a ArrayRef,
pub patch_indices: &'a ArrayRef,
pub patch_values: &'a ArrayRef,
}
impl<'a> PatchedSlotsView<'a> {
pub fn from_slots(slots: &'a [Option<ArrayRef>]) -> Self { ... }
pub fn to_owned(&self) -> PatchedSlots { ... }
}
// --- Ext trait with per-field accessors + slots_view() ---
pub trait PatchedArraySlotsExt: TypedArrayRef<Patched> {
fn inner(&self) -> &ArrayRef { ... } // indexes slots directly
fn lane_offsets(&self) -> &ArrayRef { ... }
fn patch_indices(&self) -> &ArrayRef { ... }
fn patch_values(&self) -> &ArrayRef { ... }
fn slots_view(&self) -> PatchedSlotsView<'_> { ... }
}
impl<T: TypedArrayRef<Patched>> PatchedArraySlotsExt for T {}§Slot index annotations
- Fixed fields use
#[slot(N)], whereNis the exact index of the slot. - A variadic tail uses
#[slot(N..)], whereNis the index its first slot occupies.
The annotations are validated at compile time: the fixed indices must cover
0..FIXED_COUNT exactly — no duplicates and no gaps — and a variadic tail must start
immediately after the last fixed slot. A field without a #[slot(..)] attribute is a
compile error, so the layout can never silently fall back to declaration order.
§Required vs optional slots
-
ArrayRef— the slot must be present.from_slots()panics ifNone. The ext trait accessor returns&ArrayRef. The view field is&'a ArrayRef. -
Option<ArrayRef>— the slot may be absent.from_slots()preservesNone. The ext trait accessor returnsOption<&ArrayRef>. The view field isOption<&'a ArrayRef>.
The underlying storage is always ArraySlots — the field type only
controls whether the macro inserts a .vortex_expect() unwrap or not.
§Variadic tail slots
One field may be Vec<ArrayRef>, declaring that every slot from its index onward
belongs to a homogeneous, variable-length run of required slots. This supports
encodings like Chunked ([chunk_offsets, chunks...]), Struct
([validity?, fields...]), and Union ([type_ids, children...]).
#[array_slots(Chunked)]
pub struct ChunkedSlots {
#[slot(0)]
pub chunk_offsets: ArrayRef,
#[slot(1..)]
pub chunks: Vec<ArrayRef>,
}For a struct with a variadic tail, the macro generates a different set of constants — slot count is no longer a compile-time constant:
impl ChunkedSlots {
pub const CHUNK_OFFSETS: usize = 0;
/// Offset at which the `chunks` slots begin.
pub const CHUNKS_OFFSET: usize = 1;
/// Number of fixed (non-variadic) slots.
pub const FIXED_COUNT: usize = 1;
/// Names of the fixed slots in storage order.
pub const FIXED_NAMES: [&'static str; 1] = ["chunk_offsets"];
/// Name of the slot at `idx`, e.g. "chunk_offsets" or "chunks[3]".
pub fn slot_name(idx: usize) -> String { ... }
pub fn from_slots(slots: ArraySlots) -> Self { ... }
pub fn into_slots(self) -> ArraySlots { ... }
}The view field and ext trait accessor for the tail are a vortex_array::SlotSlice,
a borrowed run of required slots supporting len(), get(), iter(), and
indexing:
pub struct ChunkedSlotsView<'a> {
pub chunk_offsets: &'a ArrayRef,
pub chunks: SlotSlice<'a>,
}
pub trait ChunkedArraySlotsExt: TypedArrayRef<Chunked> {
fn chunk_offsets(&self) -> &ArrayRef { ... }
fn chunks(&self) -> SlotSlice<'_> { ... }
fn slots_view(&self) -> ChunkedSlotsView<'_> { ... }
}