|
| 1 | +//! Examples of how to write functions and traits that operate on `ndarray` types. |
| 2 | +//! |
| 3 | +//! `ndarray` has four kinds of array types that users may interact with: |
| 4 | +//! 1. [`ArrayBase`], the owner of the layout that describes an array in memory; |
| 5 | +//! this includes [`ndarray::Array`], [`ndarray::ArcArray`], [`ndarray::ArrayView`], |
| 6 | +//! [`ndarray::RawArrayView`], and other variants. |
| 7 | +//! 2. [`ArrayRef`], which represents a read-safe, uniquely-owned look at an array. |
| 8 | +//! 3. [`RawRef`], which represents a read-unsafe, possibly-shared look at an array. |
| 9 | +//! 4. [`LayoutRef`], which represents a look at an array's underlying structure, |
| 10 | +//! but does not allow data reading of any kind. |
| 11 | +//! |
| 12 | +//! Below, we illustrate how to write functions and traits for most variants of these types. |
| 13 | +
|
| 14 | +use ndarray::{ArrayBase, ArrayRef, Data, DataMut, Dimension, LayoutRef, RawData, RawDataMut, RawRef}; |
| 15 | + |
| 16 | +/// Take an array with the most basic requirements. |
| 17 | +/// |
| 18 | +/// This function takes its data as owning. It is very rare that a user will need to specifically |
| 19 | +/// take a reference to an `ArrayBase`, rather than to one of the other four types. |
| 20 | +#[rustfmt::skip] |
| 21 | +fn takes_base_raw<S: RawData, D>(arr: ArrayBase<S, D>) -> ArrayBase<S, D> |
| 22 | +{ |
| 23 | + // These skip from a possibly-raw array to `RawRef` and `LayoutRef`, and so must go through `AsRef` |
| 24 | + takes_rawref(arr.as_ref()); // Caller uses `.as_ref` |
| 25 | + takes_rawref_asref(&arr); // Implementor uses `.as_ref` |
| 26 | + takes_layout(arr.as_ref()); // Caller uses `.as_ref` |
| 27 | + takes_layout_asref(&arr); // Implementor uses `.as_ref` |
| 28 | + |
| 29 | + arr |
| 30 | +} |
| 31 | + |
| 32 | +/// Similar to above, but allow us to read the underlying data. |
| 33 | +#[rustfmt::skip] |
| 34 | +fn takes_base_raw_mut<S: RawDataMut, D>(mut arr: ArrayBase<S, D>) -> ArrayBase<S, D> |
| 35 | +{ |
| 36 | + // These skip from a possibly-raw array to `RawRef` and `LayoutRef`, and so must go through `AsMut` |
| 37 | + takes_rawref_mut(arr.as_mut()); // Caller uses `.as_mut` |
| 38 | + takes_rawref_asmut(&mut arr); // Implementor uses `.as_mut` |
| 39 | + takes_layout_mut(arr.as_mut()); // Caller uses `.as_mut` |
| 40 | + takes_layout_asmut(&mut arr); // Implementor uses `.as_mut` |
| 41 | + |
| 42 | + arr |
| 43 | +} |
| 44 | + |
| 45 | +/// Now take an array whose data is safe to read. |
| 46 | +#[allow(dead_code)] |
| 47 | +fn takes_base<S: Data, D>(mut arr: ArrayBase<S, D>) -> ArrayBase<S, D> |
| 48 | +{ |
| 49 | + // Raw call |
| 50 | + arr = takes_base_raw(arr); |
| 51 | + |
| 52 | + // No need for AsRef, since data is safe |
| 53 | + takes_arrref(&arr); |
| 54 | + takes_rawref(&arr); |
| 55 | + takes_rawref_asref(&arr); |
| 56 | + takes_layout(&arr); |
| 57 | + takes_layout_asref(&arr); |
| 58 | + |
| 59 | + arr |
| 60 | +} |
| 61 | + |
| 62 | +/// Now, an array whose data is safe to read and that we can mutate. |
| 63 | +/// |
| 64 | +/// Notice that we include now a trait bound on `D: Dimension`; this is necessary in order |
| 65 | +/// for the `ArrayBase` to dereference to an `ArrayRef` (or to any of the other types). |
| 66 | +#[allow(dead_code)] |
| 67 | +fn takes_base_mut<S: DataMut, D: Dimension>(mut arr: ArrayBase<S, D>) -> ArrayBase<S, D> |
| 68 | +{ |
| 69 | + // Raw call |
| 70 | + arr = takes_base_raw_mut(arr); |
| 71 | + |
| 72 | + // No need for AsMut, since data is safe |
| 73 | + takes_arrref_mut(&mut arr); |
| 74 | + takes_rawref_mut(&mut arr); |
| 75 | + takes_rawref_asmut(&mut arr); |
| 76 | + takes_layout_mut(&mut arr); |
| 77 | + takes_layout_asmut(&mut arr); |
| 78 | + |
| 79 | + arr |
| 80 | +} |
| 81 | + |
| 82 | +/// Now for new stuff: we want to read (but not alter) any array whose data is safe to read. |
| 83 | +/// |
| 84 | +/// This is probably the most common functionality that one would want to write. |
| 85 | +/// As we'll see below, calling this function is very simple for `ArrayBase<S: Data, D>`. |
| 86 | +fn takes_arrref<A, D>(arr: &ArrayRef<A, D>) |
| 87 | +{ |
| 88 | + // No need for AsRef, since data is safe |
| 89 | + takes_rawref(arr); |
| 90 | + takes_rawref_asref(arr); |
| 91 | + takes_layout(arr); |
| 92 | + takes_layout_asref(arr); |
| 93 | +} |
| 94 | + |
| 95 | +/// Now we want any array whose data is safe to mutate. |
| 96 | +/// |
| 97 | +/// **Importantly**, any array passed to this function is guaranteed to uniquely point to its data. |
| 98 | +/// As a result, passing a shared array to this function will **silently** un-share the array. |
| 99 | +#[allow(dead_code)] |
| 100 | +fn takes_arrref_mut<A, D>(arr: &mut ArrayRef<A, D>) |
| 101 | +{ |
| 102 | + // Immutable call |
| 103 | + takes_arrref(arr); |
| 104 | + |
| 105 | + // No need for AsMut, since data is safe |
| 106 | + takes_rawref_mut(arr); |
| 107 | + takes_rawref_asmut(arr); |
| 108 | + takes_layout_mut(arr); |
| 109 | + takes_rawref_asmut(arr); |
| 110 | +} |
| 111 | + |
| 112 | +/// Now, we no longer care about whether we can safely read data. |
| 113 | +/// |
| 114 | +/// This is probably the rarest type to deal with, since `LayoutRef` can access and modify an array's |
| 115 | +/// shape and strides, and even do in-place slicing. As a result, `RawRef` is only for functionality |
| 116 | +/// that requires unsafe data access, something that `LayoutRef` can't do. |
| 117 | +/// |
| 118 | +/// Writing functions and traits that deal with `RawRef`s and `LayoutRef`s can be done two ways: |
| 119 | +/// 1. Directly on the types; calling these functions on arrays whose data are not known to be safe |
| 120 | +/// to dereference (i.e., raw array views or `ArrayBase<S: RawData, D>`) must explicitly call `.as_ref()`. |
| 121 | +/// 2. Via a generic with `: AsRef<RawRef<A, D>>`; doing this will allow direct calling for all `ArrayBase` and |
| 122 | +/// `ArrayRef` instances. |
| 123 | +/// We'll demonstrate #1 here for both immutable and mutable references, then #2 directly below. |
| 124 | +#[allow(dead_code)] |
| 125 | +fn takes_rawref<A, D>(arr: &RawRef<A, D>) |
| 126 | +{ |
| 127 | + takes_layout(arr); |
| 128 | + takes_layout_asref(arr); |
| 129 | +} |
| 130 | + |
| 131 | +/// Mutable, directly take `RawRef` |
| 132 | +#[allow(dead_code)] |
| 133 | +fn takes_rawref_mut<A, D>(arr: &mut RawRef<A, D>) |
| 134 | +{ |
| 135 | + takes_layout(arr); |
| 136 | + takes_layout_asmut(arr); |
| 137 | +} |
| 138 | + |
| 139 | +/// Immutable, take a generic that implements `AsRef` to `RawRef` |
| 140 | +#[allow(dead_code)] |
| 141 | +fn takes_rawref_asref<T, A, D>(_arr: &T) |
| 142 | +where T: AsRef<RawRef<A, D>> |
| 143 | +{ |
| 144 | + takes_layout(_arr.as_ref()); |
| 145 | + takes_layout_asref(_arr.as_ref()); |
| 146 | +} |
| 147 | + |
| 148 | +/// Mutable, take a generic that implements `AsMut` to `RawRef` |
| 149 | +#[allow(dead_code)] |
| 150 | +fn takes_rawref_asmut<T, A, D>(_arr: &mut T) |
| 151 | +where T: AsMut<RawRef<A, D>> |
| 152 | +{ |
| 153 | + takes_layout_mut(_arr.as_mut()); |
| 154 | + takes_layout_asmut(_arr.as_mut()); |
| 155 | +} |
| 156 | + |
| 157 | +/// Finally, there's `LayoutRef`: this type provides read and write access to an array's *structure*, but not its *data*. |
| 158 | +/// |
| 159 | +/// Practically, this means that functions that only read/modify an array's shape or strides, |
| 160 | +/// such as checking dimensionality or slicing, should take `LayoutRef`. |
| 161 | +/// |
| 162 | +/// Like `RawRef`, functions can be written either directly on `LayoutRef` or as generics with `: AsRef<LayoutRef<A, D>>>`. |
| 163 | +#[allow(dead_code)] |
| 164 | +fn takes_layout<A, D>(_arr: &LayoutRef<A, D>) {} |
| 165 | + |
| 166 | +/// Mutable, directly take `LayoutRef` |
| 167 | +#[allow(dead_code)] |
| 168 | +fn takes_layout_mut<A, D>(_arr: &mut LayoutRef<A, D>) {} |
| 169 | + |
| 170 | +/// Immutable, take a generic that implements `AsRef` to `LayoutRef` |
| 171 | +#[allow(dead_code)] |
| 172 | +fn takes_layout_asref<T: AsRef<LayoutRef<A, D>>, A, D>(_arr: &T) {} |
| 173 | + |
| 174 | +/// Mutable, take a generic that implements `AsMut` to `LayoutRef` |
| 175 | +#[allow(dead_code)] |
| 176 | +fn takes_layout_asmut<T: AsMut<LayoutRef<A, D>>, A, D>(_arr: &mut T) {} |
| 177 | + |
| 178 | +fn main() {} |
0 commit comments