Skip to content

Commit dd49b77

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

File tree

3 files changed

+126
-15
lines changed

3 files changed

+126
-15
lines changed

src/impl_owned_array.rs

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,51 @@ 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.
164+
///
165+
/// ***Panics*** if the shapes don't agree.
166+
///
167+
/// ## Example
168+
///
169+
/// ```
170+
/// use ndarray::Array;
171+
///
172+
/// // Usage example of move_into in safe code
173+
/// let mut a = Array::default((10, 10));
174+
/// let b = Array::from_shape_fn((10, 10), |(i, j)| (i + j).to_string());
175+
/// b.move_into(&mut a);
176+
/// ```
177+
pub fn move_into<'a, AM>(self, new_array: AM)
178+
where
179+
AM: Into<ArrayViewMut<'a, A, D>>,
180+
A: 'a,
181+
{
182+
// Remove generic parameter P and call the implementation
183+
let new_array = new_array.into();
184+
if mem::needs_drop::<A>() {
185+
self.move_into_needs_drop(new_array);
186+
} else {
187+
// If `A` doesn't need drop, we can overwrite the destination.
188+
// Safe because: move_into_uninit only writes initialized values
189+
unsafe {
190+
self.move_into_uninit(new_array.into_maybe_uninit())
191+
}
192+
}
193+
}
194+
195+
fn move_into_needs_drop(mut self, new_array: ArrayViewMut<A, D>) {
196+
// Simple case where `A` has a destructor: just swap values between self and new_array.
197+
// Afterwards, `self` drops full of initialized values and dropping works as usual.
198+
// This avoids moving out of owned values in `self` while at the same time managing
199+
// the dropping if the values being overwritten in `new_array`.
200+
Zip::from(&mut self).and(new_array)
201+
.for_each(|src, dst| mem::swap(src, dst));
202+
}
203+
159204
/// Move all elements from self into `new_array`, which must be of the same shape but
160205
/// can have a different memory layout. The destination is overwritten completely.
161206
///
@@ -168,20 +213,35 @@ impl<A, D> Array<A, D>
168213
/// drop of any such element, other elements may be leaked.
169214
///
170215
/// ***Panics*** if the shapes don't agree.
171-
pub fn move_into<'a, AM>(self, new_array: AM)
216+
///
217+
/// ## Example
218+
///
219+
/// ```
220+
/// use ndarray::Array;
221+
///
222+
/// let a = Array::from_iter(0..100).into_shape((10, 10)).unwrap();
223+
/// let mut b = Array::uninit((10, 10));
224+
/// a.move_into_uninit(&mut b);
225+
/// unsafe {
226+
/// // we can now promise we have fully initialized `b`.
227+
/// let b = b.assume_init();
228+
/// }
229+
/// ```
230+
pub fn move_into_uninit<'a, AM>(self, new_array: AM)
172231
where
173232
AM: Into<ArrayViewMut<'a, MaybeUninit<A>, D>>,
174233
A: 'a,
175234
{
176-
// Remove generic parameter P and call the implementation
235+
// Remove generic parameter AM and call the implementation
177236
self.move_into_impl(new_array.into())
178237
}
179238

180239
fn move_into_impl(mut self, new_array: ArrayViewMut<MaybeUninit<A>, D>) {
181240
unsafe {
182241
// Safety: copy_to_nonoverlapping cannot panic
183242
let guard = AbortIfPanic(&"move_into: moving out of owned value");
184-
// Move all reachable elements
243+
// Move all reachable elements; we move elements out of `self`.
244+
// and thus must not panic for the whole section until we call `self.data.set_len(0)`.
185245
Zip::from(self.raw_view_mut())
186246
.and(new_array)
187247
.for_each(|src, dst| {
@@ -271,7 +331,7 @@ impl<A, D> Array<A, D>
271331
// dummy array -> self.
272332
// old_self elements are moved -> new_array.
273333
let old_self = std::mem::replace(self, Self::empty());
274-
old_self.move_into(new_array.view_mut());
334+
old_self.move_into_uninit(new_array.view_mut());
275335

276336
// new_array -> self.
277337
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: 40 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;
@@ -194,6 +194,35 @@ fn move_into_empty() {
194194
}
195195
}
196196

197+
#[test]
198+
fn move_into() {
199+
// Test various memory layouts and holes while moving String elements with move_into
200+
for &use_f_order in &[false, true] {
201+
for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert
202+
for &slice in &[false, true] {
203+
let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order),
204+
|idx| format!("{:?}", idx));
205+
if slice {
206+
a.slice_collapse(s![1..-1, ..;2]);
207+
}
208+
209+
if invert_axis & 0b01 != 0 {
210+
a.invert_axis(Axis(0));
211+
}
212+
if invert_axis & 0b10 != 0 {
213+
a.invert_axis(Axis(1));
214+
}
215+
216+
let acopy = a.clone();
217+
let mut b = Array::default(a.dim().set_f(!use_f_order ^ !slice));
218+
a.move_into(&mut b);
219+
220+
assert_eq!(acopy, b);
221+
}
222+
}
223+
}
224+
}
225+
197226

198227
/// This counter can create elements, and then count and verify
199228
/// the number of which have actually been dropped again.

0 commit comments

Comments
 (0)