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