Skip to content

Commit

Permalink
move_into: Split into .move_into_unit() and .move_into_overwrite()
Browse files Browse the repository at this point in the history
  • Loading branch information
bluss committed May 9, 2021
1 parent 902bba8 commit 9f5deaf
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 13 deletions.
54 changes: 52 additions & 2 deletions src/impl_owned_array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,42 @@ impl<A> Array<A, Ix2> {
impl<A, D> Array<A, D>
where D: Dimension
{
/// Move all elements from self into `new_array`, which must be of the same shape but
/// can have a different memory layout. The destination is overwritten completely.
///
/// The destination should be a mut reference to an array or an `ArrayViewMut` with
/// `A` elements (which are **overwritten without dropping any existing value**).
///
/// Minor implementation note: Owned arrays like `self` may be sliced in place and own elements
/// that are not part of their active view; these are dropped at the end of this function,
/// after all elements in the "active view" are moved into `new_array`. If there is a panic in
/// drop of any such element, other elements may be leaked.
///
/// ***Panics*** if the shapes don't agree.
///
/// ## Example
///
/// ```
/// use ndarray::Array;
///
/// // Usage example of move_into in safe code
/// let mut a = Array::zeros((10, 10));
/// let b = Array::from_iter(0..100).into_shape((10, 10)).unwrap();
/// // make an MaybeUninit view so that we can *overwrite* into it.
/// b.move_into_overwrite(&mut a);
/// ```
pub fn move_into_overwrite<'a, AM>(self, new_array: AM)
where
AM: Into<ArrayViewMut<'a, A, D>>,
A: 'a,
{
// Remove generic parameter P and call the implementation
// safe because: no uninit elements are written into the array view, only initialized
unsafe {
self.move_into_impl(new_array.into().into_maybe_uninit())
}
}

/// Move all elements from self into `new_array`, which must be of the same shape but
/// can have a different memory layout. The destination is overwritten completely.
///
Expand All @@ -168,7 +204,21 @@ impl<A, D> Array<A, D>
/// drop of any such element, other elements may be leaked.
///
/// ***Panics*** if the shapes don't agree.
pub fn move_into<'a, AM>(self, new_array: AM)
///
/// ## Example
///
/// ```
/// use ndarray::Array;
///
/// let mut a = Array::uninit((10, 10));
/// let b = Array::from_iter(0..100).into_shape((10, 10)).unwrap();
/// b.move_inot_uninit(&mut a);
/// unsafe {
/// // we can now promise we have fully initialized `a`.
/// let a = a.assume_init();
/// }
/// ```
pub fn move_into_uninit<'a, AM>(self, new_array: AM)
where
AM: Into<ArrayViewMut<'a, MaybeUninit<A>, D>>,
A: 'a,
Expand Down Expand Up @@ -271,7 +321,7 @@ impl<A, D> Array<A, D>
// dummy array -> self.
// old_self elements are moved -> new_array.
let old_self = std::mem::replace(self, Self::empty());
old_self.move_into(new_array.view_mut());
old_self.move_into_uninit(new_array.view_mut());

// new_array -> self.
unsafe {
Expand Down
22 changes: 22 additions & 0 deletions src/impl_views/conversions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
// except according to those terms.

use alloc::slice;
use std::mem::MaybeUninit;

use crate::imp_prelude::*;

Expand Down Expand Up @@ -133,6 +134,27 @@ where
self.into_raw_view_mut().cast::<MathCell<A>>().deref_into_view()
}
}

/// Return the array view as a view of `MaybeUninit<A>` elements
///
/// This conversion leaves the elements as they were (presumably initialized), but
/// they are represented with the `MaybeUninit<A>` type. Effectively this means that
/// the elements can be overwritten without dropping the old element in its place.
/// (In some situations this is not what you want, while for `Copy` elements it makes
/// no difference at all.)
///
/// # Safety
///
/// This method allows writing uninitialized data into the view, which could leave any
/// original array that we borrow from in an inconsistent state. This is not allowed
/// when using the resulting array view.
pub(crate) unsafe fn into_maybe_uninit(self) -> ArrayViewMut<'a, MaybeUninit<A>, D> {
// Safe because: A and MaybeUninit<A> have the same representation;
// and we can go from initialized to (maybe) not unconditionally in terms of
// representation. However, the user must be careful to not write uninit elements
// through the view.
self.into_raw_view_mut().cast::<MaybeUninit<A>>().deref_into_view_mut()
}
}

/// Private array view methods
Expand Down
22 changes: 11 additions & 11 deletions tests/assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,14 @@ fn move_into_copy() {
let a = arr2(&[[1., 2.], [3., 4.]]);
let acopy = a.clone();
let mut b = Array::uninit(a.dim());
a.move_into(b.view_mut());
a.move_into_uninit(b.view_mut());
let b = unsafe { b.assume_init() };
assert_eq!(acopy, b);

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

let acopy = a.clone();
let mut b = Array::uninit(a.dim());
a.move_into(b.view_mut());
a.move_into_uninit(b.view_mut());
let b = unsafe { b.assume_init() };

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

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

let mut b = Array::uninit(a.dim());
a.move_into(b.view_mut());
a.move_into_uninit(b.view_mut());
let b = unsafe { b.assume_init() };

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

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

let mut b = Array::uninit(a.dim());
a.move_into(b.view_mut());
a.move_into_uninit(b.view_mut());
let b = unsafe { b.assume_init() };

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

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

assert_eq!(a.ndim(), 0);
let mut b = Array::uninit(a.dim());
a.move_into(b.view_mut());
a.move_into_uninit(b.view_mut());
let b = unsafe { b.assume_init() };

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

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

let total = m * n;
Expand Down

0 comments on commit 9f5deaf

Please sign in to comment.