77//! 2. [`ArrayRef`], which represents a read-safe, uniquely-owned look at an array.
88//! 3. [`RawRef`], which represents a read-unsafe, possibly-shared look at an array.
99//! 4. [`LayoutRef`], which represents a look at an array's underlying structure,
10- //! but does not allow data reading of any kind.
10+ //! but does not allow reading data of any kind.
1111//!
1212//! Below, we illustrate how to write functions and traits for most variants of these types.
1313
14- use ndarray:: { ArrayBase , ArrayRef , Data , DataMut , Dimension , LayoutRef , RawData , RawDataMut , RawRef } ;
14+ use ndarray:: { ArrayBase , ArrayRef , Data , DataMut , Dimension , LayoutRef , RawRef } ;
1515
16- /// Take an array with the most basic requirements.
16+ /// First, the newest pattern: this function accepts arrays whose data are safe to
17+ /// dereference and uniquely held.
1718///
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 >
19+ /// This is probably the most common pattern for users.
20+ /// Once we have an array reference, we can go to [`RawRef`] and [`LayoutRef`] very easily.
21+ fn takes_arrref < A , D > ( arr : & ArrayRef < A , D > )
2222{
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
23+ // Since `ArrayRef` implements `Deref` to `RawRef`, we can pass `arr` directly to a function
24+ // that takes `RawRef`. Similarly, since `RawRef` implements `Deref` to `LayoutRef`, we can pass
25+ // `arr` directly to a function that takes `LayoutRef`.
26+ takes_rawref ( arr) ; // &ArrayRef -> &RawRef
27+ takes_layout ( arr) ; // &ArrayRef -> &RawRef -> &LayoutRef
28+
29+ // We can also pass `arr` to functions that accept `RawRef` and `LayoutRef` via `AsRef`.
30+ // These alternative function signatures are important for other types, but we see that when
31+ // we have an `ArrayRef`, we can call them very simply.
32+ takes_rawref_asref ( arr) ; // &ArrayRef -> &RawRef
33+ takes_layout_asref ( arr) ; // &ArrayRef -> &LayoutRef
3034}
3135
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 >
36+ /// Now we want any array whose data is safe to mutate.
37+ ///
38+ /// Importantly, any array passed to this function is guaranteed to uniquely point to its data.
39+ /// As a result, passing a shared array to this function will silently un-share the array.
40+ /// So, ***users should only accept `&mut ArrayRef` when they want to mutate data***.
41+ /// If they just want to mutate shape and strides, use `&mut LayoutRef` or `&AsMut<LayoutRef>`.
42+ #[ allow( dead_code) ]
43+ fn takes_arrref_mut < A , D > ( arr : & mut ArrayRef < A , D > )
3544{
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`
45+ // We can do everything we did with a `&ArrayRef`
46+ takes_arrref ( arr) ;
4147
42- arr
48+ // Similarly, we can pass this to functions that accept mutable references
49+ // to our other array reference types. These first two happen via `Deref`...
50+ takes_rawref_mut ( arr) ;
51+ takes_layout_mut ( arr) ;
52+
53+ // ... and these two happen via `AsRef`.
54+ takes_rawref_asmut ( arr) ;
55+ takes_rawref_asmut ( arr) ;
4356}
4457
45- /// Now take an array whose data is safe to read.
58+ /// Now let's go back and look at the way to write functions prior to 0.17: using `ArrayBase`.
59+ ///
60+ /// This function signature says three things:
61+ /// 1. Let me take a read only reference (that's the `&`)
62+ /// 2. Of an array whose data is safe to dereference (that's the `S: Data`)
63+ /// 3. And whose data is read-only (also `S: Data`)
64+ ///
65+ /// Let's see what we can do with this array:
4666#[ allow( dead_code) ]
47- fn takes_base < S : Data , D > ( mut arr : ArrayBase < S , D > ) -> ArrayBase < S , D >
67+ fn takes_base < S : Data , D > ( arr : & ArrayBase < S , D > )
4868{
49- // Raw call
50- arr = takes_base_raw ( arr) ;
69+ // First off: we can pass it to functions that accept `&ArrayRef`.
70+ //
71+ // This is always "cheap", in the sense that even if `arr` is an
72+ // `ArcArray` that shares its data, using this call will not un-share that data.
73+ takes_arrref ( arr) ;
5174
52- // No need for AsRef, since data is safe
53- takes_arrref ( & arr ) ;
75+ // We can also pass it to functions that accept `RawRef` and `LayoutRef`
76+ // in the usual two ways:
5477 takes_rawref ( & arr) ;
55- takes_rawref_asref ( & arr) ;
5678 takes_layout ( & arr) ;
79+ //
80+ takes_rawref_asref ( & arr) ;
5781 takes_layout_asref ( & arr) ;
82+ }
5883
59- arr
84+ /// Now, let's take a mutable reference to an `ArrayBase` - but let's keep `S: Data`, such
85+ /// that we are allowed to change the _layout_ of the array, but not its data.
86+ fn takes_base_mut < S : Data , D > ( arr : & mut ArrayBase < S , D > )
87+ {
88+ // Of course we can call everything we did with a immutable reference:
89+ takes_base ( arr) ;
90+
91+ // However, we _can't_ call a function that takes `&mut ArrayRef`:
92+ // this would require mutable data access, which `S: Data` does not provide.
93+ //
94+ // takes_arrref_mut(arr);
95+ // rustc: cannot borrow data in dereference of `ArrayBase<S, D>` as mutable
96+ //
97+ // Nor can we call a function that takes `&mut RawRef`
98+ // takes_rawref_mut(arr);
99+
100+ // We can, however, call functions that take `AsMut<LayoutRef>`,
101+ // since `LayoutRef` does not provide read access to the data:
102+ takes_layout_mut ( arr. as_layout_ref_mut ( ) ) ;
103+ //
104+ takes_layout_asmut ( arr) ;
60105}
61106
62- /// Now, an array whose data is safe to read and that we can mutate .
107+ /// Finally, let's look at a mutable reference to an `ArrayBase` with `S: DataMut` .
63108///
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).
109+ /// Note that we require a constraint of `D: Dimension` to dereference to `&mut ArrayRef`.
66110#[ allow( dead_code) ]
67- fn takes_base_mut < S : DataMut , D : Dimension > ( mut arr : ArrayBase < S , D > ) -> ArrayBase < S , D >
111+ fn takes_base_data_mut < S : DataMut , D : Dimension > ( arr : & mut ArrayBase < S , D > )
68112{
69- // Raw call
70- arr = takes_base_raw_mut ( arr) ;
113+ // Of course, everything we can do with just `S: Data`:
114+ takes_base_mut ( arr) ;
115+
116+ // But also, we can now call functions that take `&mut ArrayRef`.
117+ //
118+ // NOTE: If `arr` is actually an `ArcArray` with shared data, this
119+ // will un-share the data. This can be a potentially costly operation.
120+ takes_arrref_mut ( arr) ;
121+ }
71122
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) ;
123+ /// Let's now look at writing functions for the new `LayoutRef` type. We'll do this for both
124+ /// immutable and mutable references, and we'll see how there are two different ways to accept
125+ /// these types.
126+ ///
127+ /// These functions can only read/modify an array's shape or strides,
128+ /// such as checking dimensionality or slicing, should take `LayoutRef`.
129+ ///
130+ /// Our first way is to accept an immutable reference to `LayoutRef`:
131+ #[ allow( dead_code) ]
132+ fn takes_layout < A , D > ( _arr : & LayoutRef < A , D > ) { }
78133
79- arr
80- }
134+ /// We can also directly take a mutable reference to `LayoutRef`.
135+ #[ allow( dead_code) ]
136+ fn takes_layout_mut < A , D > ( _arr : & mut LayoutRef < A , D > ) { }
81137
82- /// Now for new stuff: we want to read (but not alter) any array whose data is safe to read.
138+ /// However, the preferred way to write these functions is by accepting
139+ /// generics using `AsRef`.
83140///
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 > )
141+ /// For immutable access, writing with `AsRef` has the same benefit as usual:
142+ /// callers have nicer ergonomics, since they can just pass any type
143+ /// without having to call `.as_ref` or `.as_layout_ref`.
144+ #[ allow( dead_code) ]
145+ fn takes_layout_asref < T , A , D > ( _arr : & T )
146+ where T : AsRef < LayoutRef < A , D > > + ?Sized
87147{
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) ;
93148}
94149
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 .
150+ /// For mutable access, there is an additional reason to write with `AsMut`:
151+ /// it prevents callers who are passing in `ArcArray` or other shared array types
152+ /// from accidentally unsharing the data through a deref chain:
153+ /// `&mut ArcArray --(unshare)--> &mut ArrayRef -> &mut RawRef -> &mut LayoutRef` .
99154#[ allow( dead_code) ]
100- fn takes_arrref_mut < A , D > ( arr : & mut ArrayRef < A , D > )
155+ fn takes_layout_asmut < T , A , D > ( _arr : & mut T )
156+ where T : AsMut < LayoutRef < A , D > > + ?Sized
101157{
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) ;
110158}
111159
112- /// Now, we no longer care about whether we can safely read data.
160+ /// Finally, we have `RawRef`, where we can access and mutate the array's data, but only unsafely.
161+ /// This is important for, e.g., dealing with [`std::mem::MaybeUninit`].
113162///
114163/// This is probably the rarest type to deal with, since `LayoutRef` can access and modify an array's
115164/// shape and strides, and even do in-place slicing. As a result, `RawRef` is only for functionality
116165/// that requires unsafe data access, something that `LayoutRef` can't do.
117166///
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.
167+ /// Like `LayoutRef`, writing functions with `RawRef` can be done in a few ways.
168+ /// We start with a direct, immutable reference
124169#[ allow( dead_code) ]
125170fn takes_rawref < A , D > ( arr : & RawRef < A , D > )
126171{
127172 takes_layout ( arr) ;
128173 takes_layout_asref ( arr) ;
129174}
130175
131- /// Mutable, directly take `RawRef`
176+ /// We can also directly take a mutable reference.
132177#[ allow( dead_code) ]
133178fn takes_rawref_mut < A , D > ( arr : & mut RawRef < A , D > )
134179{
135180 takes_layout ( arr) ;
136181 takes_layout_asmut ( arr) ;
137182}
138183
139- /// Immutable, take a generic that implements `AsRef` to `RawRef`
184+ /// However, like before, the preferred way is to write with `AsRef`,
185+ /// for the same reasons as for `LayoutRef`:
140186#[ allow( dead_code) ]
141187fn takes_rawref_asref < T , A , D > ( _arr : & T )
142188where T : AsRef < RawRef < A , D > > + ?Sized
@@ -145,7 +191,7 @@ where T: AsRef<RawRef<A, D>> + ?Sized
145191 takes_layout_asref ( _arr. as_ref ( ) ) ;
146192}
147193
148- /// Mutable, take a generic that implements `AsMut` to `RawRef`
194+ /// Finally, mutably:
149195#[ allow( dead_code) ]
150196fn takes_rawref_asmut < T , A , D > ( _arr : & mut T )
151197where T : AsMut < RawRef < A , D > > + ?Sized
@@ -154,31 +200,4 @@ where T: AsMut<RawRef<A, D>> + ?Sized
154200 takes_layout_asmut ( _arr. as_mut ( ) ) ;
155201}
156202
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 , A , D > ( _arr : & T )
173- where T : AsRef < LayoutRef < A , D > > + ?Sized
174- {
175- }
176-
177- /// Mutable, take a generic that implements `AsMut` to `LayoutRef`
178- #[ allow( dead_code) ]
179- fn takes_layout_asmut < T , A , D > ( _arr : & mut T )
180- where T : AsMut < LayoutRef < A , D > > + ?Sized
181- {
182- }
183-
184203fn main ( ) { }
0 commit comments