pub struct Slice { /* private fields */ }
Expand description
Slice is a compact representation of indices into the flat representation of an n-dimensional array. Given an offset, sizes of each dimension, and strides for each dimension, Slice can compute indices into the flat array.
For example, the following describes a dense 4x4x4 array in row-major order:
let s = Slice::new(0, vec![4, 4, 4], vec![16, 4, 1]).unwrap();
assert!(s.iter().eq(0..(4 * 4 * 4)));
Slices allow easy slicing by subsetting and striding. For example, we can fix the index of the second dimension by dropping it and adding that index (multiplied by the previous size) to the offset.
let s = Slice::new(0, vec![2, 4, 2], vec![8, 2, 1]).unwrap();
let selected_index = 3;
let sub = Slice::new(2 * selected_index, vec![2, 2], vec![8, 1]).unwrap();
let coords = [[0, 0], [0, 1], [1, 0], [1, 1]];
for coord @ [x, y] in coords {
assert_eq!(
sub.location(&coord).unwrap(),
s.location(&[x, 3, y]).unwrap()
);
}
Implementations§
Source§impl Slice
impl Slice
Sourcepub fn new(
offset: usize,
sizes: Vec<usize>,
strides: Vec<usize>,
) -> Result<Self, SliceError>
pub fn new( offset: usize, sizes: Vec<usize>, strides: Vec<usize>, ) -> Result<Self, SliceError>
Create a new Slice with the provided offset, sizes, and strides. New performs validation to ensure that sizes and strides are compatible:
- They have to be the same length (i.e., same number of dimensions)
- They have to be rectangular (i.e., stride n+1 has to evenly divide into stride n)
- Strides must be nonoverlapping (each stride has to be larger than the previous space)
Sourcepub fn into_inner(self) -> (usize, Vec<usize>, Vec<usize>)
pub fn into_inner(self) -> (usize, Vec<usize>, Vec<usize>)
Deconstruct the slice into its offset, sizes, and strides.
Sourcepub fn new_row_major(sizes: impl Into<Vec<usize>>) -> Self
pub fn new_row_major(sizes: impl Into<Vec<usize>>) -> Self
Create a new slice of the given sizes in row-major order.
Sourcepub fn new_single_multi_dim_cell(dims: usize) -> Self
pub fn new_single_multi_dim_cell(dims: usize) -> Self
Create one celled slice.
Sourcepub fn offset(&self) -> usize
pub fn offset(&self) -> usize
This is the offset from which the first value in the Slice begins.
Sourcepub fn strides(&self) -> &[usize]
pub fn strides(&self) -> &[usize]
The strides of the slice; that is, the distance between each element at a given index in the underlying array.
pub fn is_contiguous(&self) -> bool
Sourcepub fn at(&self, dim: usize, index: usize) -> Result<Self, SliceError>
pub fn at(&self, dim: usize, index: usize) -> Result<Self, SliceError>
Select a single index along a dimension, removing that dimension entirely.
This reduces the dimensionality by 1 by “fixing” one coordinate to a specific value. Think of it like taking a cross-section: selecting index 2 from the first dimension of a 3D array gives you a 2D slice, like cutting a plane from a 3D space at a fixed position.
This reduces the dimensionality by 1 by “fixing” one coordinate to a specific value. The fixed coordinate’s contribution (index × stride) gets absorbed into the base offset, while the remaining dimensions keep their original strides unchanged - they still describe the same memory distances between elements.
§Example intuition
- 3D array → select
at(dim=0, index=2)
→ 2D slice (like a plane) - 2D matrix → select
at(dim=1, index=3)
→ 1D vector (like a column) - 1D vector → select
at(dim=0, index=5)
→ 0D scalar (single element)
§Arguments
dim
- The dimension index to select fromindex
- The index within that dimension
§Returns
A new slice with one fewer dimension
§Errors
IndexOutOfRange
ifdim >= self.sizes.len()
orindex >= self.sizes[dim]
Sourcepub fn select(
&self,
dim: usize,
begin: usize,
end: usize,
step: usize,
) -> Result<Self, SliceError>
pub fn select( &self, dim: usize, begin: usize, end: usize, step: usize, ) -> Result<Self, SliceError>
A slice defines a strided view; a triple (offset,
sizes,
strides`). Each coordinate maps to a flat memory
index using the formula:
index = offset + ∑ iₖ × strides[k]
where iₖ
is the coordinate in dimension k
.
The select(dim, range)
operation restricts the view to a
subrange along a single dimension. It calculates a new slice
from a base slice by updating the offset
, sizes[dim]
, and
strides[dim]
to describe a logically reindexed subregion:
offset += begin × strides[dim]
sizes[dim] = ⎡(end - begin) / step⎤
strides[dim] ×= step
This transformation preserves the strided layout and avoids
copying data. After select
, the view behaves as if indexing
starts at zero in the selected dimension, with a new length
and stride. From the user’s perspective, nothing changes;
indexing remains zero-based, and the resulting shape can be
used like any other. The transformation is internal: the
view’s offset and stride absorb the selection logic.
Sourcepub fn location(&self, coord: &[usize]) -> Result<usize, SliceError>
pub fn location(&self, coord: &[usize]) -> Result<usize, SliceError>
Return the location of the provided coordinates.
Sourcepub fn coordinates(&self, value: usize) -> Result<Vec<usize>, SliceError>
pub fn coordinates(&self, value: usize) -> Result<Vec<usize>, SliceError>
Return the coordinates of the provided value in the n-d space of this Slice.
pub fn is_empty(&self) -> bool
Sourcepub fn iter(&self) -> SliceIterator ⓘ
pub fn iter(&self) -> SliceIterator ⓘ
Iterator over the slice’s indices.
Sourcepub fn dim_iter(&self, dims: usize) -> DimSliceIterator ⓘ
pub fn dim_iter(&self, dims: usize) -> DimSliceIterator ⓘ
Iterator over sub-dimensions of the slice.
Sourcepub fn index(&self, value: usize) -> Result<usize, SliceError>
pub fn index(&self, value: usize) -> Result<usize, SliceError>
The linear index formula calculates the logical rank of a multidimensional point in a row-major flattened array, assuming dense gapless storage with zero offset:
index := Σ(coordinate[i] × ∏(sizes[j] for j > i))
For example, given a 3x2 row-major base array B:
0 1 2 1
B = 3 4 5 V = 4
6 7 8 7
Let V be the first column of B. Then,
V | loc | index
-------+-------+------
(0, 0) | 1 | 0
(1, 0) | 4 | 1
(2, 0) | 7 | 2
§Conditions Under Which loc = index
The physical offset formula computes the memory location of a
point p
as:
loc := offset + Σ(coordinate[i] × stride[i])
Let the layout be dense row-major and offset = 0. Then,
stride[i] := ∏(sizes[j] for j > i).
and substituting into the physical offset formula:
loc = Σ(coordinate[i] × stride[i])
= Σ(coordinate[i] × ∏(sizes[j] for j > i))
= index.
Thus, ∀ p = (i, j) ∈ B, loc_B(p) = index_B(p).
§See also
The [get
] function performs an inverse operation: given a
logical index in row-major order, it computes the physical
memory offset according to the slice layout. So, if the layout
is row-major then s.get(s.index(loc)) = loc
.
Sourcepub fn get(&self, index: usize) -> Result<usize, SliceError>
pub fn get(&self, index: usize) -> Result<usize, SliceError>
Given a logical index (in row-major order), return the physical memory offset of that element according to this slice’s layout.
The index is interpreted as a position in row-major traversal that is, iterating across columns within rows. This method converts logical row-major index to physical offset by:
- Decomposing index into multidimensional coordinates
- Computing offset = base + Σ(coordinate[i] × stride[i])
For example, with shape [3, 4]
(3 rows, 4 columns) and
column-major layout:
sizes = [3, 4] // rows, cols
strides = [1, 3] // column-major: down, then right
Logical matrix:
A B C D
E F G H
I J K L
Memory layout:
offset 0 → [0, 0] = A
offset 1 → [1, 0] = E
offset 2 → [2, 0] = I
offset 3 → [0, 1] = B
offset 4 → [1, 1] = F
offset 5 → [2, 1] = J
offset 6 → [0, 2] = C
offset 7 → [1, 2] = G
offset 8 → [2, 2] = K
offset 9 → [0, 3] = D
offset 10 → [1, 3] = H
offset 11 → [2, 3] = L
Then:
index = 1 → coordinate [0, 1] → offset = 0*1 + 1*3 = 3
§Errors
Returns an error if index >= product(sizes)
.
§See also
The [index
] function performs an inverse operation: given a
memory offset, it returns the logical position of that element
in the slice’s row-major iteration order.
Sourcepub fn map<T, F>(&self, mapper: F) -> MapSlice<'_, T, F>
pub fn map<T, F>(&self, mapper: F) -> MapSlice<'_, T, F>
The returned [MapSlice
] is a view of this slice, with its elements
mapped using the provided mapping function.
Sourcepub fn view(&self, new_sizes: &[usize]) -> Result<Slice, SliceError>
pub fn view(&self, new_sizes: &[usize]) -> Result<Slice, SliceError>
Returns a new Slice
with the given shape by reinterpreting
the layout of this slice.
Constructs a new shape with standard row-major strides, using the same base offset. Returns an error if the reshaped view would access coordinates not valid in the original slice.
§Requirements
- This slice must be contiguous and have offset == 0.
- The number of elements must match:
self.sizes().iter().product() == new_sizes.iter().product()
- Each flat offset in the proposed view must be valid in
self
.
§Errors
Returns SliceError::IncompatibleView
if:
- The element count differs
- The base offset is nonzero
- Any offset in the view is not reachable in the original slice
§Example
use ndslice::Slice;
let base = Slice::new_row_major(&[2, 3, 4]);
let reshaped = base.view(&[6, 4]).unwrap();
Sourcepub fn subview(
&self,
starts: &[usize],
lens: &[usize],
) -> Result<Slice, SliceError>
pub fn subview( &self, starts: &[usize], lens: &[usize], ) -> Result<Slice, SliceError>
Returns a sub-slice of self
starting at starts
, of size lens
.
Sourcepub fn enforce_embedding<'a>(
&'a self,
other: &Slice,
) -> Result<&'a Slice, SliceError>
pub fn enforce_embedding<'a>( &'a self, other: &Slice, ) -> Result<&'a Slice, SliceError>
Ensures that every storage offset used by self
is valid in
other
.
That is, for all p ∈ self:
other.coordinates(self.location(p))
is defined.
Returns self
on success, enabling fluent chaining.
§Examples
use ndslice::Slice;
let base = Slice::new(0, vec![4, 4], vec![4, 1]).unwrap();
let view = base.subview(&[1, 1], &[2, 2]).unwrap();
assert_eq!(view.enforce_embedding(&base).unwrap().len(), 4);
let small = Slice::new(0, vec![2, 2], vec![2, 1]).unwrap();
assert!(view.enforce_embedding(&small).is_err());
Trait Implementations§
Source§impl<'de> Deserialize<'de> for Slice
impl<'de> Deserialize<'de> for Slice
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl IntoIterator for &Slice
impl IntoIterator for &Slice
Source§impl ReifySlice for Slice
impl ReifySlice for Slice
Source§fn reify_slice(&self, slice: &Slice) -> Result<Selection, SliceError>
fn reify_slice(&self, slice: &Slice) -> Result<Selection, SliceError>
Constructs a Selection
expression that symbolically
matches all coordinates in the given slice
, expressed in the
coordinate system of the provided base
slice (self
).
The result is a nested sequence of range(start..end, step)
combinators that match the rectangular region covered by slice
in base coordinates. This preserves geometry and layout when
slice
is layout-aligned — that is, each of its strides is
a multiple of the corresponding base stride.
If any dimension is not layout-aligned, the slice is reified by explicitly enumerating its coordinates.
Returns dsl::false_()
if the slice is empty.
§Errors
Returns an error if:
- The base is not contiguous and row-major
- The slice lies outside the bounds of the base
§Example
use ndslice::selection::ReifySlice;
let shape = ndslice::shape!(x = 4, y = 4);
let base = shape.slice();
let selected = ndslice::select!(shape, x = 1..3, y = 2..4).unwrap();
let slice = selected.slice();
let selection = base.reify_slice(slice).unwrap();
Source§fn reify_slices<V: AsRef<[Slice]>>(
&self,
slices: V,
) -> Result<Selection, SliceError>
fn reify_slices<V: AsRef<[Slice]>>( &self, slices: V, ) -> Result<Selection, SliceError>
Converts a list of slices
into a symbolic Selection
expression over a common base
Slice
.
Each slice describes a rectangular subregion of the base. This
function reifies each slice into a nested range(.., ..)
expression in the base coordinate system and returns the union
of all such selections.
Empty slices are ignored.
§Errors
Returns an error if any slice:
- Refers to coordinates not contained within the base
§Example
use ndslice::selection::ReifySlice;
let shape = ndslice::shape!(x = 4, y = 4);
let base = shape.slice();
let a = ndslice::select!(shape, x = 0..2, y = 0..2)
.unwrap()
.slice()
.clone();
let b = ndslice::select!(shape, x = 2..4, y = 2..4)
.unwrap()
.slice()
.clone();
let sel = base.reify_slices(&[a, b]).unwrap();
Source§impl ReshapeSliceExt for Slice
impl ReshapeSliceExt for Slice
Source§fn reshape_with_limit(&self, limit: Limit) -> Slice
fn reshape_with_limit(&self, limit: Limit) -> Slice
limit
,
preserving memory layout and flat index semantics. See
reshape_with_limit
for full behavior and rationale. Read moreimpl Eq for Slice
impl StructuralPartialEq for Slice
Auto Trait Implementations§
impl Freeze for Slice
impl RefUnwindSafe for Slice
impl Send for Slice
impl Sync for Slice
impl Unpin for Slice
impl UnwindSafe for Slice
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left
is true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self
into a Left
variant of Either<Self, Self>
if into_left(&self)
returns true
.
Converts self
into a Right
variant of Either<Self, Self>
otherwise. Read more