Skip to content

Commit 9f5deaf

Browse files
committed
move_into: Split into .move_into_unit() and .move_into_overwrite()
1 parent 902bba8 commit 9f5deaf

File tree

3 files changed

+85
-13
lines changed

3 files changed

+85
-13
lines changed

src/impl_owned_array.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,42 @@ impl<A> Array<A, Ix2> {
156156
impl<A, D> Array<A, D>
157157
where D: Dimension
158158
{
159+
/// Move all elements from self into `new_array`, which must be of the same shape but
160+
/// can have a different memory layout. The destination is overwritten completely.
161+
///
162+
/// The destination should be a mut reference to an array or an `ArrayViewMut` with
163+
/// `A` elements (which are **overwritten without dropping any existing value**).
164+
///
165+
/// Minor implementation note: Owned arrays like `self` may be sliced in place and own elements
166+
/// that are not part of their active view; these are dropped at the end of this function,
167+
/// after all elements in the "active view" are moved into `new_array`. If there is a panic in
168+
/// drop of any such element, other elements may be leaked.
169+
///
170+
/// ***Panics*** if the shapes don't agree.
171+
///
172+
/// ## Example
173+
///
174+
/// ```
175+
/// use ndarray::Array;
176+
///
177+
/// // Usage example of move_into in safe code
178+
/// let mut a = Array::zeros((10, 10));
179+
/// let b = Array::from_iter(0..100).into_shape((10, 10)).unwrap();
180+
/// // make an MaybeUninit view so that we can *overwrite* into it.
181+
/// b.move_into_overwrite(&mut a);
182+
/// ```
183+
pub fn move_into_overwrite<'a, AM>(self, new_array: AM)
184+
where
185+
AM: Into<ArrayViewMut<'a, A, D>>,
186+
A: 'a,
187+
{
188+
// Remove generic parameter P and call the implementation
189+
// safe because: no uninit elements are written into the array view, only initialized
190+
unsafe {
191+
self.move_into_impl(new_array.into().into_maybe_uninit())
192+
}
193+
}
194+
159195
/// Move all elements from self into `new_array`, which must be of the same shape but
160196
/// can have a different memory layout. The destination is overwritten completely.
161197
///
@@ -168,7 +204,21 @@ impl<A, D> Array<A, D>
168204
/// drop of any such element, other elements may be leaked.
169205
///
170206
/// ***Panics*** if the shapes don't agree.
171-
pub fn move_into<'a, AM>(self, new_array: AM)
207+
///
208+
/// ## Example
209+
///
210+
/// ```
211+
/// use ndarray::Array;
212+
///
213+
/// let mut a = Array::uninit((10, 10));
214+
/// let b = Array::from_iter(0..100).into_shape((10, 10)).unwrap();
215+
/// b.move_inot_uninit(&mut a);
216+
/// unsafe {
217+
/// // we can now promise we have fully initialized `a`.
218+
/// let a = a.assume_init();
219+
/// }
220+
/// ```
221+
pub fn move_into_uninit<'a, AM>(self, new_array: AM)
172222
where
173223
AM: Into<ArrayViewMut<'a, MaybeUninit<A>, D>>,
174224
A: 'a,
@@ -271,7 +321,7 @@ impl<A, D> Array<A, D>
271321
// dummy array -> self.
272322
// old_self elements are moved -> new_array.
273323
let old_self = std::mem::replace(self, Self::empty());
274-
old_self.move_into(new_array.view_mut());
324+
old_self.move_into_uninit(new_array.view_mut());
275325

276326
// new_array -> self.
277327
unsafe {

src/impl_views/conversions.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// except according to those terms.
88

99
use alloc::slice;
10+
use std::mem::MaybeUninit;
1011

1112
use crate::imp_prelude::*;
1213

@@ -133,6 +134,27 @@ where
133134
self.into_raw_view_mut().cast::<MathCell<A>>().deref_into_view()
134135
}
135136
}
137+
138+
/// Return the array view as a view of `MaybeUninit<A>` elements
139+
///
140+
/// This conversion leaves the elements as they were (presumably initialized), but
141+
/// they are represented with the `MaybeUninit<A>` type. Effectively this means that
142+
/// the elements can be overwritten without dropping the old element in its place.
143+
/// (In some situations this is not what you want, while for `Copy` elements it makes
144+
/// no difference at all.)
145+
///
146+
/// # Safety
147+
///
148+
/// This method allows writing uninitialized data into the view, which could leave any
149+
/// original array that we borrow from in an inconsistent state. This is not allowed
150+
/// when using the resulting array view.
151+
pub(crate) unsafe fn into_maybe_uninit(self) -> ArrayViewMut<'a, MaybeUninit<A>, D> {
152+
// Safe because: A and MaybeUninit<A> have the same representation;
153+
// and we can go from initialized to (maybe) not unconditionally in terms of
154+
// representation. However, the user must be careful to not write uninit elements
155+
// through the view.
156+
self.into_raw_view_mut().cast::<MaybeUninit<A>>().deref_into_view_mut()
157+
}
136158
}
137159

138160
/// Private array view methods

tests/assign.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,14 @@ fn move_into_copy() {
4141
let a = arr2(&[[1., 2.], [3., 4.]]);
4242
let acopy = a.clone();
4343
let mut b = Array::uninit(a.dim());
44-
a.move_into(b.view_mut());
44+
a.move_into_uninit(b.view_mut());
4545
let b = unsafe { b.assume_init() };
4646
assert_eq!(acopy, b);
4747

4848
let a = arr2(&[[1., 2.], [3., 4.]]).reversed_axes();
4949
let acopy = a.clone();
5050
let mut b = Array::uninit(a.dim());
51-
a.move_into(b.view_mut());
51+
a.move_into_uninit(b.view_mut());
5252
let b = unsafe { b.assume_init() };
5353
assert_eq!(acopy, b);
5454
}
@@ -74,7 +74,7 @@ fn move_into_owned() {
7474

7575
let acopy = a.clone();
7676
let mut b = Array::uninit(a.dim());
77-
a.move_into(b.view_mut());
77+
a.move_into_uninit(b.view_mut());
7878
let b = unsafe { b.assume_init() };
7979

8080
assert_eq!(acopy, b);
@@ -85,7 +85,7 @@ fn move_into_owned() {
8585

8686
#[test]
8787
fn move_into_slicing() {
88-
// Count correct number of drops when using move_into and discontiguous arrays (with holes).
88+
// Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes).
8989
for &use_f_order in &[false, true] {
9090
for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert
9191
let counter = DropCounter::default();
@@ -102,7 +102,7 @@ fn move_into_slicing() {
102102
}
103103

104104
let mut b = Array::uninit(a.dim());
105-
a.move_into(b.view_mut());
105+
a.move_into_uninit(b.view_mut());
106106
let b = unsafe { b.assume_init() };
107107

108108
let total = m * n;
@@ -118,7 +118,7 @@ fn move_into_slicing() {
118118

119119
#[test]
120120
fn move_into_diag() {
121-
// Count correct number of drops when using move_into and discontiguous arrays (with holes).
121+
// Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes).
122122
for &use_f_order in &[false, true] {
123123
let counter = DropCounter::default();
124124
{
@@ -128,7 +128,7 @@ fn move_into_diag() {
128128
let a = a.into_diag();
129129

130130
let mut b = Array::uninit(a.dim());
131-
a.move_into(b.view_mut());
131+
a.move_into_uninit(b.view_mut());
132132
let b = unsafe { b.assume_init() };
133133

134134
let total = m * n;
@@ -143,7 +143,7 @@ fn move_into_diag() {
143143

144144
#[test]
145145
fn move_into_0dim() {
146-
// Count correct number of drops when using move_into and discontiguous arrays (with holes).
146+
// Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes).
147147
for &use_f_order in &[false, true] {
148148
let counter = DropCounter::default();
149149
{
@@ -155,7 +155,7 @@ fn move_into_0dim() {
155155

156156
assert_eq!(a.ndim(), 0);
157157
let mut b = Array::uninit(a.dim());
158-
a.move_into(b.view_mut());
158+
a.move_into_uninit(b.view_mut());
159159
let b = unsafe { b.assume_init() };
160160

161161
let total = m * n;
@@ -170,7 +170,7 @@ fn move_into_0dim() {
170170

171171
#[test]
172172
fn move_into_empty() {
173-
// Count correct number of drops when using move_into and discontiguous arrays (with holes).
173+
// Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes).
174174
for &use_f_order in &[false, true] {
175175
let counter = DropCounter::default();
176176
{
@@ -181,7 +181,7 @@ fn move_into_empty() {
181181
let a = a.slice_move(s![..0, 1..1]);
182182
assert!(a.is_empty());
183183
let mut b = Array::uninit(a.dim());
184-
a.move_into(b.view_mut());
184+
a.move_into_uninit(b.view_mut());
185185
let b = unsafe { b.assume_init() };
186186

187187
let total = m * n;

0 commit comments

Comments
 (0)