Repository: rust-ndarray/ndarray
Pull request #1004 (2021-05-16 18:10:28)

Reduce instantiated code (measured by cargo llvm-lines)
I used cargo llvm-lines with the examples, and factored out some functionality so that it
can be shared better between different generic instantiations.

This also deprecates (but does not remove) the method `RawData::_data_slice` since it could in theory be in use by someone, even if it's not public API.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 5e241f39..b2c53ca2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 
 name = "ndarray"
-version = "0.15.2"
+version = "0.15.1"
 edition = "2018"
 authors = [
   "bluss",
diff --git a/RELEASES.md b/RELEASES.md
index 386ed24d..be479c87 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -1,87 +1,3 @@
-Version 0.15.2 (2021-xx-xx Not released yet)
-===========================
-
-New features
-------------
-
-- New methods for growing/appending to owned `Array`s. These methods allow
-  building an array efficiently chunk by chunk. By [@bluss].
-
-  - `.push_row()`, `.push_column()`
-  - `.push(axis, array)`, `.append(axis, array)`
-
-  https://github.com/rust-ndarray/ndarray/pull/932 <br>
-  https://github.com/rust-ndarray/ndarray/pull/990
-
-- New reshaping method `.to_shape(...)`, called with new shape and optional
-  ordering parameter, this is the first improvement for reshaping in terms of
-  added features and increased consistency. By [@bluss].
-
-  https://github.com/rust-ndarray/ndarray/pull/982
-
-- `Array` now implements a by-value iterator, by [@bluss].
-
-  https://github.com/rust-ndarray/ndarray/pull/986
-
-- New methods `.move_into()` and `.move_into_uninit()` which allow assigning
-  into an array by moving values into them, by [@bluss].
-
-  https://github.com/rust-ndarray/ndarray/pull/932 <br>
-  https://github.com/rust-ndarray/ndarray/pull/997
-
-- New method `.remove_index()` for owned arrays by [@bluss]
-
-  https://github.com/rust-ndarray/ndarray/pull/967
-
-- New constructor `build_uninit` which makes it easier to initialize
-  uninitialized arrays in a way that's generic over all owned array kinds.
-  By [@bluss].
-
-  https://github.com/rust-ndarray/ndarray/pull/1001
-
-Enhancements
-------------
-
-- Preserve the allocation of the input array in some more cases for arithmetic ops by [@SparrowLii]
-
-  https://github.com/rust-ndarray/ndarray/pull/963
-
-- Improve broadcasting performance for &array + &array arithmetic ops by [@SparrowLii]
-
-  https://github.com/rust-ndarray/ndarray/pull/965
-
-Bug fixes
----------
-
-- Fix an error in construction of empty array with negative strides, by [@jturner314].
-
-  https://github.com/rust-ndarray/ndarray/pull/998
-
-- Fix minor performance bug with loop order selection in Zip by [@bluss]
-
-  https://github.com/rust-ndarray/ndarray/pull/977
-
-API changes
------------
-
-- Add dimension getters to `Shape` and `StrideShape` by [@stokhos]
-
-  https://github.com/rust-ndarray/ndarray/pull/978
-
-Other changes
--------------
-
-- Rustdoc now uses the ndarray logo that [@jturner314] created previously
-
-  https://github.com/rust-ndarray/ndarray/pull/981
-
-- Minor doc changes by [@stokhos], [@cassiersg] and [@jturner314]
-
-  https://github.com/rust-ndarray/ndarray/pull/968 <br>
-  https://github.com/rust-ndarray/ndarray/pull/971 <br>
-  https://github.com/rust-ndarray/ndarray/pull/974
-
-
 Version 0.15.1 (2021-03-29)
 ===========================
 
@@ -1370,7 +1286,6 @@ Earlier releases
 [@LukeMathWalker]: https://github.com/LukeMathWalker
 [@acj]: https://github.com/acj
 [@andrei-papou]: https://github.com/andrei-papou
-[@cassiersg]: https://github.com/cassiersg
 [@dam5h]: https://github.com/dam5h
 [@d-dorazio]: https://github.com/d-dorazio
 [@Eijebong]: https://github.com/Eijebong
@@ -1386,7 +1301,6 @@ Earlier releases
 [@rth]: https://github.com/rth
 [@sebasv]: https://github.com/sebasv
 [@SparrowLii]: https://github.com/SparrowLii
-[@stokhos]: https://github.com/stokhos
 [@termoshtt]: https://github.com/termoshtt
 [@TheLortex]: https://github.com/TheLortex
 [@viniciusd]: https://github.com/viniciusd
diff --git a/src/data_traits.rs b/src/data_traits.rs
index 7d9f15b0..bd540b1b 100644
--- a/src/data_traits.rs
+++ b/src/data_traits.rs
@@ -33,8 +33,12 @@ pub unsafe trait RawData: Sized {
 
     #[doc(hidden)]
     // This method is only used for debugging
+    #[deprecated(note="Unused", since="0.15.2")]
     fn _data_slice(&self) -> Option<&[Self::Elem]>;
 
+    #[doc(hidden)]
+    fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool;
+
     private_decl! {}
 }
 
@@ -146,9 +150,15 @@ pub unsafe trait DataMut: Data + RawDataMut {
 
 unsafe impl<A> RawData for RawViewRepr<*const A> {
     type Elem = A;
+
+    #[inline]
     fn _data_slice(&self) -> Option<&[A]> {
         None
     }
+
+    #[inline(always)]
+    fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true }
+
     private_impl! {}
 }
 
@@ -160,9 +170,15 @@ unsafe impl<A> RawDataClone for RawViewRepr<*const A> {
 
 unsafe impl<A> RawData for RawViewRepr<*mut A> {
     type Elem = A;
+
+    #[inline]
     fn _data_slice(&self) -> Option<&[A]> {
         None
     }
+
+    #[inline(always)]
+    fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true }
+
     private_impl! {}
 }
 
@@ -192,6 +208,11 @@ unsafe impl<A> RawData for OwnedArcRepr<A> {
     fn _data_slice(&self) -> Option<&[A]> {
         Some(self.0.as_slice())
     }
+
+    fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool {
+        self.0._is_pointer_inbounds(self_ptr)
+    }
+
     private_impl! {}
 }
 
@@ -274,9 +295,18 @@ unsafe impl<A> RawDataClone for OwnedArcRepr<A> {
 
 unsafe impl<A> RawData for OwnedRepr<A> {
     type Elem = A;
+
     fn _data_slice(&self) -> Option<&[A]> {
         Some(self.as_slice())
     }
+
+    fn _is_pointer_inbounds(&self, self_ptr: *const Self::Elem) -> bool {
+        let slc = self.as_slice();
+        let ptr = slc.as_ptr() as *mut A;
+        let end = unsafe { ptr.add(slc.len()) };
+        self_ptr >= ptr && self_ptr <= end
+    }
+
     private_impl! {}
 }
 
@@ -340,9 +370,15 @@ where
 
 unsafe impl<'a, A> RawData for ViewRepr<&'a A> {
     type Elem = A;
+
+    #[inline]
     fn _data_slice(&self) -> Option<&[A]> {
         None
     }
+
+    #[inline(always)]
+    fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true }
+
     private_impl! {}
 }
 
@@ -364,9 +400,15 @@ unsafe impl<'a, A> RawDataClone for ViewRepr<&'a A> {
 
 unsafe impl<'a, A> RawData for ViewRepr<&'a mut A> {
     type Elem = A;
+
+    #[inline]
     fn _data_slice(&self) -> Option<&[A]> {
         None
     }
+
+    #[inline(always)]
+    fn _is_pointer_inbounds(&self, _ptr: *const Self::Elem) -> bool { true }
+
     private_impl! {}
 }
 
@@ -458,12 +500,23 @@ unsafe impl<A> DataOwned for OwnedArcRepr<A> {
 
 unsafe impl<'a, A> RawData for CowRepr<'a, A> {
     type Elem = A;
+
     fn _data_slice(&self) -> Option<&[A]> {
+        #[allow(deprecated)]
         match self {
             CowRepr::View(view) => view._data_slice(),
             CowRepr::Owned(data) => data._data_slice(),
         }
     }
+
+    #[inline]
+    fn _is_pointer_inbounds(&self, ptr: *const Self::Elem) -> bool {
+        match self {
+            CowRepr::View(view) => view._is_pointer_inbounds(ptr),
+            CowRepr::Owned(data) => data._is_pointer_inbounds(ptr),
+        }
+    }
+
     private_impl! {}
 }
 
diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs
index 732006e2..7bb92892 100644
--- a/src/dimension/mod.rs
+++ b/src/dimension/mod.rs
@@ -668,6 +668,56 @@ pub fn slices_intersect<D: Dimension>(
     true
 }
 
+pub(crate) fn is_layout_c<D: Dimension>(dim: &D, strides: &D) -> bool {
+    if let Some(1) = D::NDIM {
+        return strides[0] == 1 || dim[0] <= 1;
+    }
+
+    for &d in dim.slice() {
+        if d == 0 {
+            return true;
+        }
+    }
+
+    let mut contig_stride = 1_isize;
+    // check all dimensions -- a dimension of length 1 can have unequal strides
+    for (&dim, &s) in izip!(dim.slice().iter().rev(), strides.slice().iter().rev()) {
+        if dim != 1 {
+            let s = s as isize;
+            if s != contig_stride {
+                return false;
+            }
+            contig_stride *= dim as isize;
+        }
+    }
+    true
+}
+
+pub(crate) fn is_layout_f<D: Dimension>(dim: &D, strides: &D) -> bool {
+    if let Some(1) = D::NDIM {
+        return strides[0] == 1 || dim[0] <= 1;
+    }
+
+    for &d in dim.slice() {
+        if d == 0 {
+            return true;
+        }
+    }
+
+    let mut contig_stride = 1_isize;
+    // check all dimensions -- a dimension of length 1 can have unequal strides
+    for (&dim, &s) in izip!(dim.slice(), strides.slice()) {
+        if dim != 1 {
+            let s = s as isize;
+            if s != contig_stride {
+                return false;
+            }
+            contig_stride *= dim as isize;
+        }
+    }
+    true
+}
+
 pub fn merge_axes<D>(dim: &mut D, strides: &mut D, take: Axis, into: Axis) -> bool
 where
     D: Dimension,
diff --git a/src/impl_methods.rs b/src/impl_methods.rs
index c441efcc..21470ec0 100644
--- a/src/impl_methods.rs
+++ b/src/impl_methods.rs
@@ -1382,23 +1382,7 @@ where
     /// Return `false` otherwise, i.e. the array is possibly not
     /// contiguous in memory, it has custom strides, etc.
     pub fn is_standard_layout(&self) -> bool {
-        fn is_standard_layout<D: Dimension>(dim: &D, strides: &D) -> bool {
-            if let Some(1) = D::NDIM {
-                return strides[0] == 1 || dim[0] <= 1;
-            }
-            if dim.slice().iter().any(|&d| d == 0) {
-                return true;
-            }
-            let defaults = dim.default_strides();
-            // check all dimensions -- a dimension of length 1 can have unequal strides
-            for (&dim, &s, &ds) in izip!(dim.slice(), strides.slice(), defaults.slice()) {
-                if dim != 1 && s != ds {
-                    return false;
-                }
-            }
-            true
-        }
-        is_standard_layout(&self.dim, &self.strides)
+        dimension::is_layout_c(&self.dim, &self.strides)
     }
 
     /// Return true if the array is known to be contiguous.
@@ -2151,17 +2135,7 @@ where
     }
 
     pub(crate) fn pointer_is_inbounds(&self) -> bool {
-        match self.data._data_slice() {
-            None => {
-                // special case for non-owned views
-                true
-            }
-            Some(slc) => {
-                let ptr = slc.as_ptr() as *mut A;
-                let end = unsafe { ptr.add(slc.len()) };
-                self.ptr.as_ptr() >= ptr && self.ptr.as_ptr() <= end
-            }
-        }
+        self.data._is_pointer_inbounds(self.as_ptr())
     }
 
     /// Perform an elementwise assigment to `self` from `rhs`.
diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs
index d148a115..63af6391 100644
--- a/src/impl_owned_array.rs
+++ b/src/impl_owned_array.rs
@@ -1,6 +1,5 @@
 
 use alloc::vec::Vec;
-use std::mem;
 use std::mem::MaybeUninit;
 
 use rawpointer::PointerExt;
@@ -34,7 +33,7 @@ impl<A> Array<A, Ix0> {
     /// assert_eq!(scalar, Foo);
     /// ```
     pub fn into_scalar(self) -> A {
-        let size = mem::size_of::<A>();
+        let size = ::std::mem::size_of::<A>();
         if size == 0 {
             // Any index in the `Vec` is fine since all elements are identical.
             self.data.into_vec().remove(0)
@@ -156,51 +155,6 @@ 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.
-    ///
-    /// ***Panics*** if the shapes don't agree.
-    ///
-    /// ## Example
-    ///
-    /// ```
-    /// use ndarray::Array;
-    ///
-    /// // Usage example of move_into in safe code
-    /// let mut a = Array::default((10, 10));
-    /// let b = Array::from_shape_fn((10, 10), |(i, j)| (i + j).to_string());
-    /// b.move_into(&mut a);
-    /// ```
-    pub fn move_into<'a, AM>(self, new_array: AM)
-    where
-        AM: Into<ArrayViewMut<'a, A, D>>,
-        A: 'a,
-    {
-        // Remove generic parameter P and call the implementation
-        let new_array = new_array.into();
-        if mem::needs_drop::<A>() {
-            self.move_into_needs_drop(new_array);
-        } else {
-            // If `A` doesn't need drop, we can overwrite the destination.
-            // Safe because: move_into_uninit only writes initialized values
-            unsafe {
-                self.move_into_uninit(new_array.into_maybe_uninit())
-            }
-        }
-    }
-
-    fn move_into_needs_drop(mut self, new_array: ArrayViewMut<A, D>) {
-        // Simple case where `A` has a destructor: just swap values between self and new_array.
-        // Afterwards, `self` drops full of initialized values and dropping works as usual.
-        // This avoids moving out of owned values in `self` while at the same time managing
-        // the dropping if the values being overwritten in `new_array`.
-        Zip::from(&mut self).and(new_array)
-            .for_each(|src, dst| mem::swap(src, dst));
-    }
-
     /// 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.
     ///
@@ -213,26 +167,12 @@ impl<A, D> Array<A, D>
     /// drop of any such element, other elements may be leaked.
     ///
     /// ***Panics*** if the shapes don't agree.
-    ///
-    /// ## Example
-    ///
-    /// ```
-    /// use ndarray::Array;
-    ///
-    /// let a = Array::from_iter(0..100).into_shape((10, 10)).unwrap();
-    /// let mut b = Array::uninit((10, 10));
-    /// a.move_into_uninit(&mut b);
-    /// unsafe {
-    ///     // we can now promise we have fully initialized `b`.
-    ///     let b = b.assume_init();
-    /// }
-    /// ```
-    pub fn move_into_uninit<'a, AM>(self, new_array: AM)
+    pub fn move_into<'a, AM>(self, new_array: AM)
     where
         AM: Into<ArrayViewMut<'a, MaybeUninit<A>, D>>,
         A: 'a,
     {
-        // Remove generic parameter AM and call the implementation
+        // Remove generic parameter P and call the implementation
         self.move_into_impl(new_array.into())
     }
 
@@ -240,8 +180,7 @@ impl<A, D> Array<A, D>
         unsafe {
             // Safety: copy_to_nonoverlapping cannot panic
             let guard = AbortIfPanic(&"move_into: moving out of owned value");
-            // Move all reachable elements; we move elements out of `self`.
-            // and thus must not panic for the whole section until we call `self.data.set_len(0)`.
+            // Move all reachable elements
             Zip::from(self.raw_view_mut())
                 .and(new_array)
                 .for_each(|src, dst| {
@@ -269,7 +208,7 @@ impl<A, D> Array<A, D>
         let data_len = self.data.len();
 
         let has_unreachable_elements = self_len != data_len;
-        if !has_unreachable_elements || mem::size_of::<A>() == 0 || !mem::needs_drop::<A>() {
+        if !has_unreachable_elements || std::mem::size_of::<A>() == 0 {
             unsafe {
                 self.data.set_len(0);
             }
@@ -331,7 +270,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_uninit(new_array.view_mut());
+        old_self.move_into(new_array.view_mut());
 
         // new_array -> self.
         unsafe {
diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs
index 5dba16d9..cfd7f9aa 100644
--- a/src/impl_views/conversions.rs
+++ b/src/impl_views/conversions.rs
@@ -7,7 +7,6 @@
 // except according to those terms.
 
 use alloc::slice;
-use std::mem::MaybeUninit;
 
 use crate::imp_prelude::*;
 
@@ -134,27 +133,6 @@ 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
diff --git a/src/zip/mod.rs b/src/zip/mod.rs
index 7277f99f..1ba0181c 100644
--- a/src/zip/mod.rs
+++ b/src/zip/mod.rs
@@ -21,6 +21,7 @@ use crate::partial::Partial;
 
 use crate::indexes::{indices, Indices};
 use crate::split_at::{SplitPreference, SplitAt};
+use crate::dimension;
 
 pub use self::ndproducer::{NdProducer, IntoNdProducer, Offset};
 
@@ -51,33 +52,38 @@ where
     private_decl! {}
 }
 
+/// Compute `Layout` hints for array shape dim, strides
+fn array_layout<D: Dimension>(dim: &D, strides: &D) -> Layout {
+    let n = dim.ndim();
+    if dimension::is_layout_c(dim, strides) {
+        // effectively one-dimensional => C and F layout compatible
+        if n <= 1 || dim.slice().iter().filter(|&&len| len > 1).count() <= 1 {
+            Layout::one_dimensional()
+        } else {
+            Layout::c()
+        }
+    } else if n > 1 && dimension::is_layout_f(dim, strides) {
+        Layout::f()
+    } else if n > 1 {
+        if dim[0] > 1 && strides[0] == 1 {
+            Layout::fpref()
+        } else if dim[n - 1] > 1 && strides[n - 1] == 1 {
+            Layout::cpref()
+        } else {
+            Layout::none()
+        }
+    } else {
+        Layout::none()
+    }
+}
+
 impl<S, D> ArrayBase<S, D>
 where
     S: RawData,
     D: Dimension,
 {
     pub(crate) fn layout_impl(&self) -> Layout {
-        let n = self.ndim();
-        if self.is_standard_layout() {
-            // effectively one-dimensional => C and F layout compatible
-            if n <= 1 || self.shape().iter().filter(|&&len| len > 1).count() <= 1 {
-                Layout::one_dimensional()
-            } else {
-                Layout::c()
-            }
-        } else if n > 1 && self.raw_view().reversed_axes().is_standard_layout() {
-            Layout::f()
-        } else if n > 1 {
-            if self.len_of(Axis(0)) > 1 && self.stride_of(Axis(0)) == 1 {
-                Layout::fpref()
-            } else if self.len_of(Axis(n - 1)) > 1 && self.stride_of(Axis(n - 1)) == 1 {
-                Layout::cpref()
-            } else {
-                Layout::none()
-            }
-        } else {
-            Layout::none()
-        }
+        array_layout(&self.dim, &self.strides)
     }
 }
 


Test patch:
diff --git a/tests/assign.rs b/tests/assign.rs
index 5c300943..19156cce 100644
--- a/tests/assign.rs
+++ b/tests/assign.rs
@@ -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_uninit(b.view_mut());
+    a.move_into(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_uninit(b.view_mut());
+    a.move_into(b.view_mut());
     let b = unsafe { b.assume_init() };
     assert_eq!(acopy, b);
 }
@@ -74,7 +74,7 @@ fn move_into_owned() {
 
                 let acopy = a.clone();
                 let mut b = Array::uninit(a.dim());
-                a.move_into_uninit(b.view_mut());
+                a.move_into(b.view_mut());
                 let b = unsafe { b.assume_init() };
 
                 assert_eq!(acopy, b);
@@ -85,7 +85,7 @@ fn move_into_owned() {
 
 #[test]
 fn move_into_slicing() {
-    // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes).
+    // Count correct number of drops when using move_into 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();
@@ -102,7 +102,7 @@ fn move_into_slicing() {
                 }
 
                 let mut b = Array::uninit(a.dim());
-                a.move_into_uninit(b.view_mut());
+                a.move_into(b.view_mut());
                 let b = unsafe { b.assume_init() };
 
                 let total = m * n;
@@ -118,7 +118,7 @@ fn move_into_slicing() {
 
 #[test]
 fn move_into_diag() {
-    // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes).
+    // Count correct number of drops when using move_into and discontiguous arrays (with holes).
     for &use_f_order in &[false, true] {
         let counter = DropCounter::default();
         {
@@ -128,7 +128,7 @@ fn move_into_diag() {
             let a = a.into_diag();
 
             let mut b = Array::uninit(a.dim());
-            a.move_into_uninit(b.view_mut());
+            a.move_into(b.view_mut());
             let b = unsafe { b.assume_init() };
 
             let total = m * n;
@@ -143,7 +143,7 @@ fn move_into_diag() {
 
 #[test]
 fn move_into_0dim() {
-    // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes).
+    // Count correct number of drops when using move_into and discontiguous arrays (with holes).
     for &use_f_order in &[false, true] {
         let counter = DropCounter::default();
         {
@@ -155,7 +155,7 @@ fn move_into_0dim() {
 
             assert_eq!(a.ndim(), 0);
             let mut b = Array::uninit(a.dim());
-            a.move_into_uninit(b.view_mut());
+            a.move_into(b.view_mut());
             let b = unsafe { b.assume_init() };
 
             let total = m * n;
@@ -170,7 +170,7 @@ fn move_into_0dim() {
 
 #[test]
 fn move_into_empty() {
-    // Count correct number of drops when using move_into_uninit and discontiguous arrays (with holes).
+    // Count correct number of drops when using move_into and discontiguous arrays (with holes).
     for &use_f_order in &[false, true] {
         let counter = DropCounter::default();
         {
@@ -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_uninit(b.view_mut());
+            a.move_into(b.view_mut());
             let b = unsafe { b.assume_init() };
 
             let total = m * n;
@@ -194,35 +194,6 @@ fn move_into_empty() {
     }
 }
 
-#[test]
-fn move_into() {
-    // Test various memory layouts and holes while moving String elements with move_into
-    for &use_f_order in &[false, true] {
-        for &invert_axis in &[0b00, 0b01, 0b10, 0b11] { // bitmask for axis to invert
-            for &slice in &[false, true] {
-                let mut a = Array::from_shape_fn((5, 4).set_f(use_f_order),
-                                                 |idx| format!("{:?}", idx));
-                if slice {
-                    a.slice_collapse(s![1..-1, ..;2]);
-                }
-
-                if invert_axis & 0b01 != 0 {
-                    a.invert_axis(Axis(0));
-                }
-                if invert_axis & 0b10 != 0 {
-                    a.invert_axis(Axis(1));
-                }
-
-                let acopy = a.clone();
-                let mut b = Array::default(a.dim().set_f(!use_f_order ^ !slice));
-                a.move_into(&mut b);
-
-                assert_eq!(acopy, b);
-            }
-        }
-    }
-}
-
 
 /// This counter can create elements, and then count and verify
 /// the number of which have actually been dropped again.