Skip to content

Commit 1229eea

Browse files
authored
Merge pull request #803 from rust-ndarray/generic-maybe-uninit
Add maybe_uninit constructor and .assume_init() method
2 parents ea0b69e + 684b9c4 commit 1229eea

8 files changed

+228
-47
lines changed

examples/sort-axis.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,26 +102,31 @@ where
102102
assert_eq!(axis_len, perm.indices.len());
103103
debug_assert!(perm.correct());
104104

105-
let mut v = Vec::with_capacity(self.len());
106-
let mut result;
105+
let mut result = Array::maybe_uninit(self.dim());
107106

108107
// panic-critical begin: we must not panic
109108
unsafe {
110-
v.set_len(self.len());
111-
result = Array::from_shape_vec_unchecked(self.dim(), v);
109+
// logically move ownership of all elements from self into result
110+
// the result realizes this ownership at .assume_init() further down
111+
let mut moved_elements = 0;
112112
for i in 0..axis_len {
113113
let perm_i = perm.indices[i];
114114
Zip::from(result.index_axis_mut(axis, perm_i))
115115
.and(self.index_axis(axis, i))
116-
.apply(|to, from| copy_nonoverlapping(from, to, 1));
116+
.apply(|to, from| {
117+
copy_nonoverlapping(from, to.as_mut_ptr(), 1);
118+
moved_elements += 1;
119+
});
117120
}
118121
// forget moved array elements but not its vec
122+
// old_storage drops empty
119123
let mut old_storage = self.into_raw_vec();
120124
old_storage.set_len(0);
121-
// old_storage drops empty
125+
126+
debug_assert_eq!(result.len(), moved_elements);
127+
result.assume_init()
122128
}
123129
// panic-critical end
124-
result
125130
}
126131
}
127132

src/data_repr.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
use std::mem;
3+
use std::mem::ManuallyDrop;
34
use std::ptr::NonNull;
45
use std::slice;
56
use crate::extension::nonnull;
@@ -17,22 +18,20 @@ pub struct OwnedRepr<A> {
1718
}
1819

1920
impl<A> OwnedRepr<A> {
20-
pub(crate) fn from(mut v: Vec<A>) -> Self {
21+
pub(crate) fn from(v: Vec<A>) -> Self {
22+
let mut v = ManuallyDrop::new(v);
2123
let len = v.len();
2224
let capacity = v.capacity();
2325
let ptr = nonnull::nonnull_from_vec_data(&mut v);
24-
mem::forget(v);
2526
Self {
2627
ptr,
2728
len,
2829
capacity,
2930
}
3031
}
3132

32-
pub(crate) fn into_vec(mut self) -> Vec<A> {
33-
let v = self.take_as_vec();
34-
mem::forget(self);
35-
v
33+
pub(crate) fn into_vec(self) -> Vec<A> {
34+
ManuallyDrop::new(self).take_as_vec()
3635
}
3736

3837
pub(crate) fn as_slice(&self) -> &[A] {

src/data_traits.rs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,3 +528,38 @@ unsafe impl<'a, A> Data for CowRepr<'a, A> {
528528
}
529529

530530
unsafe impl<'a, A> DataMut for CowRepr<'a, A> where A: Clone {}
531+
532+
/// Array representation trait.
533+
///
534+
/// The RawDataSubst trait maps the element type of array storage, while
535+
/// keeping the same kind of storage.
536+
///
537+
/// For example, `RawDataSubst<B>` can map the type `OwnedRepr<A>` to `OwnedRepr<B>`.
538+
pub trait RawDataSubst<A>: RawData {
539+
/// The resulting array storage of the same kind but substituted element type
540+
type Output: RawData<Elem = A>;
541+
}
542+
543+
impl<A, B> RawDataSubst<B> for OwnedRepr<A> {
544+
type Output = OwnedRepr<B>;
545+
}
546+
547+
impl<A, B> RawDataSubst<B> for OwnedArcRepr<A> {
548+
type Output = OwnedArcRepr<B>;
549+
}
550+
551+
impl<A, B> RawDataSubst<B> for RawViewRepr<*const A> {
552+
type Output = RawViewRepr<*const B>;
553+
}
554+
555+
impl<A, B> RawDataSubst<B> for RawViewRepr<*mut A> {
556+
type Output = RawViewRepr<*mut B>;
557+
}
558+
559+
impl<'a, A: 'a, B: 'a> RawDataSubst<B> for ViewRepr<&'a A> {
560+
type Output = ViewRepr<&'a B>;
561+
}
562+
563+
impl<'a, A: 'a, B: 'a> RawDataSubst<B> for ViewRepr<&'a mut A> {
564+
type Output = ViewRepr<&'a mut B>;
565+
}

src/impl_constructors.rs

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,9 @@ where
467467

468468
/// Create an array with uninitalized elements, shape `shape`.
469469
///
470+
/// Prefer to use [`maybe_uninit()`](ArrayBase::maybe_uninit) if possible, because it is
471+
/// easier to use correctly.
472+
///
470473
/// **Panics** if the number of elements in `shape` would overflow isize.
471474
///
472475
/// ### Safety
@@ -524,7 +527,66 @@ where
524527
S: DataOwned<Elem = MaybeUninit<A>>,
525528
D: Dimension,
526529
{
527-
pub(crate) fn maybe_uninit<Sh>(shape: Sh) -> Self
530+
/// Create an array with uninitalized elements, shape `shape`.
531+
///
532+
/// The uninitialized elements of type `A` are represented by the type `MaybeUninit<A>`,
533+
/// an easier way to handle uninit values correctly.
534+
///
535+
/// Only *when* the array is completely initialized with valid elements, can it be
536+
/// converted to an array of `A` elements using [`.assume_init()`].
537+
///
538+
/// **Panics** if the number of elements in `shape` would overflow isize.
539+
///
540+
/// ### Safety
541+
///
542+
/// The whole of the array must be initialized before it is converted
543+
/// using [`.assume_init()`] or otherwise traversed.
544+
///
545+
/// ### Examples
546+
///
547+
/// It is possible to assign individual values through `*elt = MaybeUninit::new(value)`
548+
/// and so on.
549+
///
550+
/// [`.assume_init()`]: ArrayBase::assume_init
551+
///
552+
/// ```
553+
/// use ndarray::{s, Array2};
554+
/// use ndarray::Zip;
555+
/// use ndarray::Axis;
556+
///
557+
/// // Example Task: Let's create a transposed copy of the input
558+
///
559+
/// fn shift_by_two(a: &Array2<f32>) -> Array2<f32> {
560+
/// // create an uninitialized array
561+
/// let mut b = Array2::maybe_uninit(a.dim());
562+
///
563+
/// // two first columns in b are two last in a
564+
/// // rest of columns in b are the initial columns in a
565+
///
566+
/// assign_to(a.slice(s![.., -2..]), b.slice_mut(s![.., ..2]));
567+
/// assign_to(a.slice(s![.., 2..]), b.slice_mut(s![.., ..-2]));
568+
///
569+
/// // Now we can promise that `b` is safe to use with all operations
570+
/// unsafe {
571+
/// b.assume_init()
572+
/// }
573+
/// }
574+
///
575+
/// use ndarray::{IntoNdProducer, AssignElem};
576+
///
577+
/// fn assign_to<'a, P1, P2, A>(from: P1, to: P2)
578+
/// where P1: IntoNdProducer<Item = &'a A>,
579+
/// P2: IntoNdProducer<Dim = P1::Dim>,
580+
/// P2::Item: AssignElem<A>,
581+
/// A: Clone + 'a
582+
/// {
583+
/// Zip::from(from)
584+
/// .apply_assign_into(to, A::clone);
585+
/// }
586+
///
587+
/// # shift_by_two(&Array2::zeros((8, 8)));
588+
/// ```
589+
pub fn maybe_uninit<Sh>(shape: Sh) -> Self
528590
where
529591
Sh: ShapeBuilder<Dim = D>,
530592
{

src/impl_owned_array.rs

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
1-
use std::mem::MaybeUninit;
2-
use std::mem::transmute;
31

42
use crate::imp_prelude::*;
5-
use crate::OwnedRepr;
63

74
/// Methods specific to `Array0`.
85
///
@@ -61,33 +58,3 @@ where
6158
self.data.into_vec()
6259
}
6360
}
64-
65-
/// Methods specific to `Array` of `MaybeUninit`.
66-
///
67-
/// ***See also all methods for [`ArrayBase`]***
68-
///
69-
/// [`ArrayBase`]: struct.ArrayBase.html
70-
impl<A, D> Array<MaybeUninit<A>, D>
71-
where
72-
D: Dimension,
73-
{
74-
/// Assert that the array's storage's elements are all fully initialized, and conver
75-
/// the array from element type `MaybeUninit<A>` to `A`.
76-
pub(crate) unsafe fn assume_init(self) -> Array<A, D> {
77-
// NOTE: Fully initialized includes elements not reachable in current slicing/view.
78-
//
79-
// Should this method be generalized to all array types?
80-
// (Will need a way to map the RawData<Elem=X> to RawData<Elem=Y> of same kind)
81-
82-
let Array { data, ptr, dim, strides } = self;
83-
let data = transmute::<OwnedRepr<MaybeUninit<A>>, OwnedRepr<A>>(data);
84-
let ptr = ptr.cast::<A>();
85-
86-
Array {
87-
data,
88-
ptr,
89-
dim,
90-
strides,
91-
}
92-
}
93-
}

src/impl_special_element_types.rs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2020 bluss and ndarray developers.
2+
//
3+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6+
// option. This file may not be copied, modified, or distributed
7+
// except according to those terms.
8+
9+
use std::mem::size_of;
10+
use std::mem::ManuallyDrop;
11+
use std::mem::MaybeUninit;
12+
13+
use crate::imp_prelude::*;
14+
use crate::RawDataSubst;
15+
16+
17+
/// Methods specific to arrays with `MaybeUninit` elements.
18+
///
19+
/// ***See also all methods for [`ArrayBase`]***
20+
///
21+
/// [`ArrayBase`]: struct.ArrayBase.html
22+
impl<A, S, D> ArrayBase<S, D>
23+
where
24+
S: RawDataSubst<A, Elem=MaybeUninit<A>>,
25+
D: Dimension,
26+
{
27+
/// **Promise** that the array's elements are all fully initialized, and convert
28+
/// the array from element type `MaybeUninit<A>` to `A`.
29+
///
30+
/// For example, it can convert an `Array<MaybeUninit<f64>, D>` to `Array<f64, D>`.
31+
///
32+
/// ## Safety
33+
///
34+
/// Safe to use if all the array's elements have been initialized.
35+
///
36+
/// Note that for owned and shared ownership arrays, the promise must include all of the
37+
/// array's storage; it is for example possible to slice these in place, but that must
38+
/// only be done after all elements have been initialized.
39+
pub unsafe fn assume_init(self) -> ArrayBase<<S as RawDataSubst<A>>::Output, D> {
40+
// NOTE: Fully initialized includes elements not reachable in current slicing/view.
41+
42+
let ArrayBase { data, ptr, dim, strides } = self;
43+
44+
// transmute from storage of MaybeUninit<A> to storage of A
45+
let data = unlimited_transmute::<S, S::Output>(data);
46+
let ptr = ptr.cast::<A>();
47+
48+
ArrayBase {
49+
data,
50+
ptr,
51+
dim,
52+
strides,
53+
}
54+
}
55+
}
56+
57+
/// Transmute from A to B.
58+
///
59+
/// Like transmute, but does not have the compile-time size check which blocks
60+
/// using regular transmute for "S to S::Output".
61+
///
62+
/// **Panics** if the size of A and B are different.
63+
unsafe fn unlimited_transmute<A, B>(data: A) -> B {
64+
assert_eq!(size_of::<A>(), size_of::<B>());
65+
let old_data = ManuallyDrop::new(data);
66+
(&*old_data as *const A as *const B).read()
67+
}

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ pub use crate::aliases::*;
159159
#[allow(deprecated)]
160160
pub use crate::data_traits::{
161161
Data, DataClone, DataMut, DataOwned, DataShared, RawData, RawDataClone, RawDataMut,
162+
RawDataSubst,
162163
};
163164

164165
mod free_functions;
@@ -1483,6 +1484,7 @@ mod impl_constructors;
14831484

14841485
mod impl_methods;
14851486
mod impl_owned_array;
1487+
mod impl_special_element_types;
14861488

14871489
/// Private Methods
14881490
impl<A, S, D> ArrayBase<S, D>

tests/array-construct.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use defmac::defmac;
99
use ndarray::prelude::*;
10+
use ndarray::Zip;
1011

1112
#[test]
1213
fn test_from_shape_fn() {
@@ -194,3 +195,46 @@ fn deny_wraparound_uninit() {
194195
let _five_large = Array::<f32, _>::uninitialized((3, 7, 29, 36760123, 823996703));
195196
}
196197
}
198+
199+
200+
#[test]
201+
fn maybe_uninit_1() {
202+
use std::mem::MaybeUninit;
203+
204+
unsafe {
205+
// Array
206+
type Mat<D> = Array<MaybeUninit<f32>, D>;
207+
208+
let mut a = Mat::maybe_uninit((10, 10));
209+
a.mapv_inplace(|_| MaybeUninit::new(1.));
210+
211+
let a_init = a.assume_init();
212+
assert_eq!(a_init, Array2::from_elem(a_init.dim(), 1.));
213+
214+
// ArcArray
215+
type ArcMat<D> = ArcArray<MaybeUninit<f32>, D>;
216+
217+
let mut a = ArcMat::maybe_uninit((10, 10));
218+
a.mapv_inplace(|_| MaybeUninit::new(1.));
219+
let a2 = a.clone();
220+
221+
let a_init = a.assume_init();
222+
assert_eq!(a_init, Array2::from_elem(a_init.dim(), 1.));
223+
224+
// ArrayView
225+
let av_init = a2.view().assume_init();
226+
assert_eq!(av_init, Array2::from_elem(a_init.dim(), 1.));
227+
228+
// RawArrayViewMut
229+
let mut a = Mat::maybe_uninit((10, 10));
230+
let v = a.raw_view_mut();
231+
Zip::from(v)
232+
.apply(|ptr| *(*ptr).as_mut_ptr() = 1.);
233+
234+
let u = a.raw_view_mut().assume_init();
235+
236+
Zip::from(u)
237+
.apply(|ptr| assert_eq!(*ptr, 1.));
238+
239+
}
240+
}

0 commit comments

Comments
 (0)