Skip to content

Commit 5a25737

Browse files
authored
Adds an array reference type (#1440)
See #1440 for more information, especially [this comment](#1440 (comment)).
1 parent 1866e91 commit 5a25737

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2554
-651
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@ target/
44
# Editor settings
55
.vscode
66
.idea
7+
8+
# Apple details
9+
**/.DS_Store

crates/blas-tests/tests/oper.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,7 @@ fn gen_mat_mul()
280280
cv = c.view_mut();
281281
}
282282

283-
let answer_part = alpha * reference_mat_mul(&av, &bv) + beta * &cv;
283+
let answer_part: Array<f64, _> = alpha * reference_mat_mul(&av, &bv) + beta * &cv;
284284
answer.slice_mut(s![..;s1, ..;s2]).assign(&answer_part);
285285

286286
general_mat_mul(alpha, &av, &bv, beta, &mut cv);

examples/axis_ops.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use ndarray::prelude::*;
1313
/// it corresponds to their order in memory.
1414
///
1515
/// Errors if array has a 0-stride axis
16-
fn regularize<A, D>(a: &mut Array<A, D>) -> Result<(), &'static str>
16+
fn regularize<A, D>(a: &mut ArrayRef<A, D>) -> Result<(), &'static str>
1717
where
1818
D: Dimension,
1919
A: ::std::fmt::Debug,

examples/convo.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ type Kernel3x3<A> = [[A; 3]; 3];
1414

1515
#[inline(never)]
1616
#[cfg(feature = "std")]
17-
fn conv_3x3<F>(a: &ArrayView2<'_, F>, out: &mut ArrayViewMut2<'_, F>, kernel: &Kernel3x3<F>)
17+
fn conv_3x3<F>(a: &ArrayRef2<F>, out: &mut ArrayRef2<F>, kernel: &Kernel3x3<F>)
1818
where F: Float
1919
{
2020
let (n, m) = a.dim();

examples/functions_and_traits.rs

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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

Comments
 (0)