Repository: rust-ndarray/ndarray
Pull request #997 (2021-05-09 19:21:00)

move_into: Split into methods `.move_into()` and `.move_into_uninit()`.
Two methods for `Array`:

- `.move_into(impl Into<ArrayViewMut<A, ..>>)`
- `.move_into_uninit(impl Into<ArrayViewMut<MaybeUninit<A>, ..>>)`

The `uninit` version is an optimization - it is more efficient since we don't need to keep track of the overwriting of existing values in the array. However, we optimize so that if `A` has no drop, they should be equivalent. So the user can use whichever fits best without worrying so much.

The new `move_into`  turned out to be very simple: just swap the elements in the old and new array. This means we never have a partially moved-from array to keep track of and no headaches. We simply delegate over to the non-drop method if we can. We also use a `needs_drop` check in `move_into_uninit` to skip all the complicated dropping code if the element type doesn't have drop.


Patch:
diff --git a/benches/to_shape.rs b/benches/to_shape.rs
deleted file mode 100644
index a048eb77..00000000
--- a/benches/to_shape.rs
+++ /dev/null
@@ -1,106 +0,0 @@
-#![feature(test)]
-
-extern crate test;
-use test::Bencher;
-
-use ndarray::prelude::*;
-use ndarray::Order;
-
-#[bench]
-fn to_shape2_1(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape(4 * 5).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape2_2_same(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape((4, 5)).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape2_2_flip(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape((5, 4)).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape2_3(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape((2, 5, 2)).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape3_1(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((3, 4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape(3 * 4 * 5).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape3_2_order(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((3, 4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape((12, 5)).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape3_2_outoforder(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((3, 4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape((4, 15)).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape3_3c(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((3, 4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape((3, 4, 5)).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape3_3f(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((3, 4, 5).f());
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape(((3, 4, 5), Order::F)).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape3_4c(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((3, 4, 5));
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape(((2, 3, 2, 5), Order::C)).unwrap()
-    });
-}
-
-#[bench]
-fn to_shape3_4f(bench: &mut Bencher) {
-    let a = Array::<f32, _>::zeros((3, 4, 5).f());
-    let view = a.view();
-    bench.iter(|| {
-        view.to_shape(((2, 3, 2, 5), Order::F)).unwrap()
-    });
-}
diff --git a/src/arrayformat.rs b/src/arrayformat.rs
index 5998a0c1..c1ca352e 100644
--- a/src/arrayformat.rs
+++ b/src/arrayformat.rs
@@ -50,8 +50,10 @@ impl FormatOptions {
             self.axis_collapse_limit = std::usize::MAX;
             self.axis_collapse_limit_next_last = std::usize::MAX;
             self.axis_collapse_limit_last = std::usize::MAX;
+            self
+        } else {
+            self
         }
-        self
     }
 
     /// Axis length collapse limit before ellipsizing, where `axis_rindex` is
diff --git a/src/data_traits.rs b/src/data_traits.rs
index 7d9f15b0..7ac63d54 100644
--- a/src/data_traits.rs
+++ b/src/data_traits.rs
@@ -252,7 +252,6 @@ unsafe impl<A> Data for OwnedArcRepr<A> {
         }
     }
 
-    #[allow(clippy::wrong_self_convention)]
     fn to_shared<D>(self_: &ArrayBase<Self, D>) -> ArrayBase<OwnedArcRepr<Self::Elem>, D>
     where
         Self::Elem: Clone,
diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs
index 732006e2..f4f46e76 100644
--- a/src/dimension/mod.rs
+++ b/src/dimension/mod.rs
@@ -9,10 +9,10 @@
 use crate::error::{from_kind, ErrorKind, ShapeError};
 use crate::slice::SliceArg;
 use crate::{Ix, Ixs, Slice, SliceInfoElem};
-use crate::shape_builder::Strides;
 use num_integer::div_floor;
 
 pub use self::axes::{Axes, AxisDescription};
+pub(crate) use self::axes::axes_of;
 pub use self::axis::Axis;
 pub use self::broadcast::DimMax;
 pub use self::conversion::IntoDimension;
@@ -23,8 +23,7 @@ pub use self::ndindex::NdIndex;
 pub use self::ops::DimAdd;
 pub use self::remove_axis::RemoveAxis;
 
-pub(crate) use self::axes::axes_of;
-pub(crate) use self::reshape::reshape_dim;
+use crate::shape_builder::Strides;
 
 use std::isize;
 use std::mem;
@@ -41,8 +40,6 @@ mod dynindeximpl;
 mod ndindex;
 mod ops;
 mod remove_axis;
-pub(crate) mod reshape;
-mod sequence;
 
 /// Calculate offset from `Ix` stride converting sign properly
 #[inline(always)]
@@ -235,12 +232,11 @@ where
 /// units of `A` and in units of bytes between the least address and greatest
 /// address accessible by moving along all axes does not exceed `isize::MAX`.
 ///
-/// Warning: This function is sufficient to check the invariants of ArrayBase
-/// only if the pointer to the first element of the array is chosen such that
-/// the element with the smallest memory address is at the start of the
-/// allocation. (In other words, the pointer to the first element of the array
-/// must be computed using `offset_from_low_addr_ptr_to_logical_ptr` so that
-/// negative strides are correctly handled.)
+/// Warning: This function is sufficient to check the invariants of ArrayBase only
+/// if the pointer to the first element of the array is chosen such that the element
+/// with the smallest memory address is at the start of data. (In other words, the
+/// pointer to the first element of the array must be computed using offset_from_ptr_to_memory
+/// so that negative strides are correctly handled.)
 pub(crate) fn can_index_slice<A, D: Dimension>(
     data: &[A],
     dim: &D,
@@ -408,19 +404,17 @@ fn to_abs_slice(axis_len: usize, slice: Slice) -> (usize, usize, isize) {
     (start, end, step)
 }
 
-/// Returns the offset from the lowest-address element to the logically first
-/// element.
-pub fn offset_from_low_addr_ptr_to_logical_ptr<D: Dimension>(dim: &D, strides: &D) -> usize {
-    let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (&d, &s)| {
-        let s = s as isize;
-        if s < 0 && d > 1 {
-            _offset - s * (d as isize - 1)
+/// This function computes the offset from the logically first element to the first element in
+/// memory of the array. The result is always <= 0.
+pub fn offset_from_ptr_to_memory<D: Dimension>(dim: &D, strides: &D) -> isize {
+    let offset = izip!(dim.slice(), strides.slice()).fold(0, |_offset, (d, s)| {
+        if (*s as isize) < 0 {
+            _offset + *s as isize * (*d as isize - 1)
         } else {
             _offset
         }
     });
-    debug_assert!(offset >= 0);
-    offset as usize
+    offset
 }
 
 /// Modify dimension, stride and return data pointer offset
diff --git a/src/dimension/reshape.rs b/src/dimension/reshape.rs
deleted file mode 100644
index c6e08848..00000000
--- a/src/dimension/reshape.rs
+++ /dev/null
@@ -1,241 +0,0 @@
-
-use crate::{Dimension, Order, ShapeError, ErrorKind};
-use crate::dimension::sequence::{Sequence, SequenceMut, Forward, Reverse};
-
-#[inline]
-pub(crate) fn reshape_dim<D, E>(from: &D, strides: &D, to: &E, order: Order)
-    -> Result<E, ShapeError>
-where
-    D: Dimension,
-    E: Dimension,
-{
-    debug_assert_eq!(from.ndim(), strides.ndim());
-    let mut to_strides = E::zeros(to.ndim());
-    match order {
-        Order::RowMajor => {
-            reshape_dim_c(&Forward(from), &Forward(strides),
-                          &Forward(to), Forward(&mut to_strides))?;
-        }
-        Order::ColumnMajor => {
-            reshape_dim_c(&Reverse(from), &Reverse(strides),
-                          &Reverse(to), Reverse(&mut to_strides))?;
-        }
-    }
-    Ok(to_strides)
-}
-
-/// Try to reshape an array with dimensions `from_dim` and strides `from_strides` to the new
-/// dimension `to_dim`, while keeping the same layout of elements in memory. The strides needed
-/// if this is possible are stored into `to_strides`.
-///
-/// This function uses RowMajor index ordering if the inputs are read in the forward direction
-/// (index 0 is axis 0 etc) and ColumnMajor index ordering if the inputs are read in reversed
-/// direction (as made possible with the Sequence trait).
-/// 
-/// Preconditions:
-///
-/// 1. from_dim and to_dim are valid dimensions (product of all non-zero axes
-/// fits in isize::MAX).
-/// 2. from_dim and to_dim are don't have any axes that are zero (that should be handled before
-///    this function).
-/// 3. `to_strides` should be an all-zeros or all-ones dimension of the right dimensionality
-/// (but it will be overwritten after successful exit of this function).
-///
-/// This function returns:
-///
-/// - IncompatibleShape if the two shapes are not of matching number of elements
-/// - IncompatibleLayout if the input shape and stride can not be remapped to the output shape
-///   without moving the array data into a new memory layout.
-/// - Ok if the from dim could be mapped to the new to dim.
-fn reshape_dim_c<D, E, E2>(from_dim: &D, from_strides: &D, to_dim: &E, mut to_strides: E2)
-    -> Result<(), ShapeError>
-where
-    D: Sequence<Output=usize>,
-    E: Sequence<Output=usize>,
-    E2: SequenceMut<Output=usize>,
-{
-    // cursor indexes into the from and to dimensions
-    let mut fi = 0;  // index into `from_dim`
-    let mut ti = 0;  // index into `to_dim`.
-
-    while fi < from_dim.len() && ti < to_dim.len() {
-        let mut fd = from_dim[fi];
-        let mut fs = from_strides[fi] as isize;
-        let mut td = to_dim[ti];
-
-        if fd == td {
-            to_strides[ti] = from_strides[fi];
-            fi += 1;
-            ti += 1;
-            continue
-        }
-
-        if fd == 1 {
-            fi += 1;
-            continue;
-        }
-
-        if td == 1 {
-            to_strides[ti] = 1;
-            ti += 1;
-            continue;
-        }
-
-        if fd == 0 || td == 0 {
-            debug_assert!(false, "zero dim not handled by this function");
-            return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape));
-        }
-
-        // stride times element count is to be distributed out over a combination of axes.
-        let mut fstride_whole = fs * (fd as isize);
-        let mut fd_product = fd;  // cumulative product of axis lengths in the combination (from)
-        let mut td_product = td;  // cumulative product of axis lengths in the combination (to)
-
-        // The two axis lengths are not a match, so try to combine multiple axes
-        // to get it to match up.
-        while fd_product != td_product {
-            if fd_product < td_product {
-                // Take another axis on the from side
-                fi += 1;
-                if fi >= from_dim.len() {
-                    return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape));
-                }
-                fd = from_dim[fi];
-                fd_product *= fd;
-                if fd > 1 {
-                    let fs_old = fs;
-                    fs = from_strides[fi] as isize;
-                    // check if this axis and the next are contiguous together
-                    if fs_old != fd as isize * fs {
-                        return Err(ShapeError::from_kind(ErrorKind::IncompatibleLayout));
-                    }
-                }
-            } else {
-                // Take another axis on the `to` side
-                // First assign the stride to the axis we leave behind
-                fstride_whole /= td as isize;
-                to_strides[ti] = fstride_whole as usize;
-                ti += 1;
-                if ti >= to_dim.len() {
-                    return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape));
-                }
-
-                td = to_dim[ti];
-                td_product *= td;
-            }
-        }
-
-        fstride_whole /= td as isize;
-        to_strides[ti] = fstride_whole as usize;
-
-        fi += 1;
-        ti += 1;
-    }
-
-    // skip past 1-dims at the end
-    while fi < from_dim.len() && from_dim[fi] == 1 {
-        fi += 1;
-    }
-
-    while ti < to_dim.len() && to_dim[ti] == 1 {
-        to_strides[ti] = 1;
-        ti += 1;
-    }
-
-    if fi < from_dim.len() || ti < to_dim.len() {
-        return Err(ShapeError::from_kind(ErrorKind::IncompatibleShape));
-    }
-
-    Ok(())
-}
-
-#[cfg(feature = "std")]
-#[test]
-fn test_reshape() {
-    use crate::Dim;
-
-    macro_rules! test_reshape {
-        (fail $order:ident from $from:expr, $stride:expr, to $to:expr) => {
-            let res = reshape_dim(&Dim($from), &Dim($stride), &Dim($to), Order::$order);
-            println!("Reshape {:?} {:?} to {:?}, order {:?}\n  => {:?}",
-                     $from, $stride, $to, Order::$order, res);
-            let _res = res.expect_err("Expected failed reshape");
-        };
-        (ok $order:ident from $from:expr, $stride:expr, to $to:expr, $to_stride:expr) => {{
-            let res = reshape_dim(&Dim($from), &Dim($stride), &Dim($to), Order::$order);
-            println!("Reshape {:?} {:?} to {:?}, order {:?}\n  => {:?}",
-                     $from, $stride, $to, Order::$order, res);
-            println!("default stride for from dim: {:?}", Dim($from).default_strides());
-            println!("default stride for to dim: {:?}", Dim($to).default_strides());
-            let res = res.expect("Expected successful reshape");
-            assert_eq!(res, Dim($to_stride), "mismatch in strides");
-        }};
-    }
-
-    test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [1, 2, 3], [6, 3, 1]);
-    test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [2, 3], [3, 1]);
-    test_reshape!(ok C from [1, 2, 3], [6, 3, 1], to [6], [1]);
-    test_reshape!(fail C from [1, 2, 3], [6, 3, 1], to [1]);
-    test_reshape!(fail F from [1, 2, 3], [6, 3, 1], to [1]);
-
-    test_reshape!(ok C from [6], [1], to [3, 2], [2, 1]);
-    test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [4, 15], [15, 1]);
-
-    test_reshape!(ok C from [4, 4, 4], [16, 4, 1], to [16, 4], [4, 1]);
-
-    test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 4, 1], [8, 4, 1, 1]);
-    test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 4], [8, 4, 1]);
-    test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 2, 2], [8, 4, 2, 1]);
-
-    test_reshape!(ok C from [4, 4], [4, 1], to [2, 2, 1, 4], [8, 4, 1, 1]);
-
-    test_reshape!(ok C from [4, 4, 4], [16, 4, 1], to [16, 4], [4, 1]);
-    test_reshape!(ok C from [3, 4, 4], [16, 4, 1], to [3, 16], [16, 1]);
-
-    test_reshape!(ok C from [4, 4], [8, 1], to [2, 2, 2, 2], [16, 8, 2, 1]);
-
-    test_reshape!(fail C from [4, 4], [8, 1], to [2, 1, 4, 2]);
-
-    test_reshape!(ok C from [16], [4], to [2, 2, 4], [32, 16, 4]);
-    test_reshape!(ok C from [16], [-4isize as usize], to [2, 2, 4],
-                  [-32isize as usize, -16isize as usize, -4isize as usize]);
-    test_reshape!(ok F from [16], [4], to [2, 2, 4], [4, 8, 16]);
-    test_reshape!(ok F from [16], [-4isize as usize], to [2, 2, 4],
-                  [-4isize as usize, -8isize as usize, -16isize as usize]);
-
-    test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [12, 5], [5, 1]);
-    test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [4, 15], [15, 1]);
-    test_reshape!(fail F from [3, 4, 5], [20, 5, 1], to [4, 15]);
-    test_reshape!(ok C from [3, 4, 5, 7], [140, 35, 7, 1], to [28, 15], [15, 1]);
-
-    // preserve stride if shape matches
-    test_reshape!(ok C from [10], [2], to [10], [2]);
-    test_reshape!(ok F from [10], [2], to [10], [2]);
-    test_reshape!(ok C from [2, 10], [1, 2], to [2, 10], [1, 2]);
-    test_reshape!(ok F from [2, 10], [1, 2], to [2, 10], [1, 2]);
-    test_reshape!(ok C from [3, 4, 5], [20, 5, 1], to [3, 4, 5], [20, 5, 1]);
-    test_reshape!(ok F from [3, 4, 5], [20, 5, 1], to [3, 4, 5], [20, 5, 1]);
-
-    test_reshape!(ok C from [3, 4, 5], [4, 1, 1], to [12, 5], [1, 1]);
-    test_reshape!(ok F from [3, 4, 5], [1, 3, 12], to [12, 5], [1, 12]);
-    test_reshape!(ok F from [3, 4, 5], [1, 3, 1], to [12, 5], [1, 1]);
-
-    // broadcast shapes
-    test_reshape!(ok C from [3, 4, 5, 7], [0, 0, 7, 1], to [12, 35], [0, 1]);
-    test_reshape!(fail C from [3, 4, 5, 7], [0, 0, 7, 1], to [28, 15]);
-
-    // one-filled shapes
-    test_reshape!(ok C from [10], [1], to [1, 10, 1, 1, 1], [1, 1, 1, 1, 1]);
-    test_reshape!(ok F from [10], [1], to [1, 10, 1, 1, 1], [1, 1, 1, 1, 1]);
-    test_reshape!(ok C from [1, 10], [10, 1], to [1, 10, 1, 1, 1], [10, 1, 1, 1, 1]);
-    test_reshape!(ok F from [1, 10], [10, 1], to [1, 10, 1, 1, 1], [10, 1, 1, 1, 1]);
-    test_reshape!(ok C from [1, 10], [1, 1], to [1, 5, 1, 1, 2], [1, 2, 2, 2, 1]);
-    test_reshape!(ok F from [1, 10], [1, 1], to [1, 5, 1, 1, 2], [1, 1, 5, 5, 5]);
-    test_reshape!(ok C from [10, 1, 1, 1, 1], [1, 1, 1, 1, 1], to [10], [1]);
-    test_reshape!(ok F from [10, 1, 1, 1, 1], [1, 1, 1, 1, 1], to [10], [1]);
-    test_reshape!(ok C from [1, 5, 1, 2, 1], [1, 2, 1, 1, 1], to [10], [1]);
-    test_reshape!(fail F from [1, 5, 1, 2, 1], [1, 2, 1, 1, 1], to [10]);
-    test_reshape!(ok F from [1, 5, 1, 2, 1], [1, 1, 1, 5, 1], to [10], [1]);
-    test_reshape!(fail C from [1, 5, 1, 2, 1], [1, 1, 1, 5, 1], to [10]);
-}
-
diff --git a/src/dimension/sequence.rs b/src/dimension/sequence.rs
deleted file mode 100644
index 835e00d1..00000000
--- a/src/dimension/sequence.rs
+++ /dev/null
@@ -1,109 +0,0 @@
-use std::ops::Index;
-use std::ops::IndexMut;
-
-use crate::dimension::Dimension;
-
-pub(in crate::dimension) struct Forward<D>(pub(crate) D);
-pub(in crate::dimension) struct Reverse<D>(pub(crate) D);
-
-impl<D> Index<usize> for Forward<&D>
-where
-    D: Dimension,
-{
-    type Output = usize;
-
-    #[inline]
-    fn index(&self, index: usize) -> &usize {
-        &self.0[index]
-    }
-}
-
-impl<D> Index<usize> for Forward<&mut D>
-where
-    D: Dimension,
-{
-    type Output = usize;
-
-    #[inline]
-    fn index(&self, index: usize) -> &usize {
-        &self.0[index]
-    }
-}
-
-impl<D> IndexMut<usize> for Forward<&mut D>
-where
-    D: Dimension,
-{
-    #[inline]
-    fn index_mut(&mut self, index: usize) -> &mut usize {
-        &mut self.0[index]
-    }
-}
-
-impl<D> Index<usize> for Reverse<&D>
-where
-    D: Dimension,
-{
-    type Output = usize;
-
-    #[inline]
-    fn index(&self, index: usize) -> &usize {
-        &self.0[self.len() - index - 1]
-    }
-}
-
-impl<D> Index<usize> for Reverse<&mut D>
-where
-    D: Dimension,
-{
-    type Output = usize;
-
-    #[inline]
-    fn index(&self, index: usize) -> &usize {
-        &self.0[self.len() - index - 1]
-    }
-}
-
-impl<D> IndexMut<usize> for Reverse<&mut D>
-where
-    D: Dimension,
-{
-    #[inline]
-    fn index_mut(&mut self, index: usize) -> &mut usize {
-        let len = self.len();
-        &mut self.0[len - index - 1]
-    }
-}
-
-/// Indexable sequence with length
-pub(in crate::dimension) trait Sequence: Index<usize> {
-    fn len(&self) -> usize;
-}
-
-/// Indexable sequence with length (mut)
-pub(in crate::dimension) trait SequenceMut: Sequence + IndexMut<usize> { }
-
-impl<D> Sequence for Forward<&D> where D: Dimension {
-    #[inline]
-    fn len(&self) -> usize { self.0.ndim() }
-}
-
-impl<D> Sequence for Forward<&mut D> where D: Dimension {
-    #[inline]
-    fn len(&self) -> usize { self.0.ndim() }
-}
-
-impl<D> SequenceMut for Forward<&mut D> where D: Dimension { }
-
-impl<D> Sequence for Reverse<&D> where D: Dimension {
-    #[inline]
-    fn len(&self) -> usize { self.0.ndim() }
-}
-
-impl<D> Sequence for Reverse<&mut D> where D: Dimension {
-    #[inline]
-    fn len(&self) -> usize { self.0.ndim() }
-}
-
-impl<D> SequenceMut for Reverse<&mut D> where D: Dimension { }
-
diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs
index 62b95169..3336ec33 100644
--- a/src/impl_constructors.rs
+++ b/src/impl_constructors.rs
@@ -20,7 +20,7 @@ use alloc::vec;
 use alloc::vec::Vec;
 
 use crate::dimension;
-use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr;
+use crate::dimension::offset_from_ptr_to_memory;
 use crate::error::{self, ShapeError};
 use crate::extension::nonnull::nonnull_from_vec_data;
 use crate::imp_prelude::*;
@@ -492,7 +492,7 @@ where
         // debug check for issues that indicates wrong use of this constructor
         debug_assert!(dimension::can_index_slice(&v, &dim, &strides).is_ok());
 
-        let ptr = nonnull_from_vec_data(&mut v).add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides));
+        let ptr = nonnull_from_vec_data(&mut v).offset(-offset_from_ptr_to_memory(&dim, &strides));
         ArrayBase::from_data_ptr(DataOwned::new(v), ptr).with_strides_dim(strides, dim)
     }
 
@@ -530,7 +530,7 @@ where
     /// ### Safety
     ///
     /// The whole of the array must be initialized before it is converted
-    /// using [`.assume_init()`] or otherwise traversed/read with the element type `A`.
+    /// using [`.assume_init()`] or otherwise traversed.
     ///
     /// ### Examples
     ///
@@ -580,10 +580,10 @@ where
     /// The uninitialized elements of type `A` are represented by the type `MaybeUninit<A>`,
     /// an easier way to handle uninit values correctly.
     ///
-    /// The `builder` closure gets unshared access to the array through a view and can use it to
-    /// modify the array before it is returned. This allows initializing the array for any owned
-    /// array type (avoiding clone requirements for copy-on-write, because the array is unshared
-    /// when initially created).
+    /// The `builder` closure gets unshared access to the array through a raw view
+    /// and can use it to modify the array before it is returned. This allows initializing
+    /// the array for any owned array type (avoiding clone requirements for copy-on-write,
+    /// because the array is unshared when initially created).
     ///
     /// Only *when* the array is completely initialized with valid elements, can it be
     /// converted to an array of `A` elements using [`.assume_init()`].
@@ -593,18 +593,17 @@ where
     /// ### Safety
     ///
     /// The whole of the array must be initialized before it is converted
-    /// using [`.assume_init()`] or otherwise traversed/read with the element type `A`.
+    /// using [`.assume_init()`] or otherwise traversed.
     ///
-    /// [`.assume_init()`]: ArrayBase::assume_init
-    pub fn build_uninit<Sh, F>(shape: Sh, builder: F) -> ArrayBase<S::MaybeUninit, D>
+    pub(crate) fn build_uninit<Sh, F>(shape: Sh, builder: F) -> ArrayBase<S::MaybeUninit, D>
     where
         Sh: ShapeBuilder<Dim = D>,
-        F: FnOnce(ArrayViewMut<MaybeUninit<A>, D>),
+        F: FnOnce(RawArrayViewMut<MaybeUninit<A>, D>),
     {
         let mut array = Self::uninit(shape);
         // Safe because: the array is unshared here
         unsafe {
-            builder(array.raw_view_mut_unchecked().deref_into_view_mut());
+            builder(array.raw_view_mut_unchecked());
         }
         array
     }
diff --git a/src/impl_methods.rs b/src/impl_methods.rs
index c441efcc..9ef4277d 100644
--- a/src/impl_methods.rs
+++ b/src/impl_methods.rs
@@ -20,17 +20,14 @@ use crate::dimension;
 use crate::dimension::IntoDimension;
 use crate::dimension::{
     abs_index, axes_of, do_slice, merge_axes, move_min_stride_axis_to_last,
-    offset_from_low_addr_ptr_to_logical_ptr, size_of_shape_checked, stride_offset, Axes,
+    offset_from_ptr_to_memory, size_of_shape_checked, stride_offset, Axes,
 };
 use crate::dimension::broadcast::co_broadcast;
-use crate::dimension::reshape_dim;
 use crate::error::{self, ErrorKind, ShapeError, from_kind};
 use crate::math_cell::MathCell;
 use crate::itertools::zip;
-use crate::AxisDescription;
-use crate::order::Order;
-use crate::shape_builder::ShapeArg;
 use crate::zip::{IntoNdProducer, Zip};
+use crate::AxisDescription;
 
 use crate::iter::{
     AxisChunksIter, AxisChunksIterMut, AxisIter, AxisIterMut, ExactChunks, ExactChunksMut,
@@ -1539,10 +1536,10 @@ where
         S: Data,
     {
         if self.is_contiguous() {
-            let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides);
+            let offset = offset_from_ptr_to_memory(&self.dim, &self.strides);
             unsafe {
                 Some(slice::from_raw_parts(
-                    self.ptr.sub(offset).as_ptr(),
+                    self.ptr.offset(offset).as_ptr(),
                     self.len(),
                 ))
             }
@@ -1568,10 +1565,10 @@ where
     {
         if self.is_contiguous() {
             self.ensure_unique();
-            let offset = offset_from_low_addr_ptr_to_logical_ptr(&self.dim, &self.strides);
+            let offset = offset_from_ptr_to_memory(&self.dim, &self.strides);
             unsafe {
                 Ok(slice::from_raw_parts_mut(
-                    self.ptr.sub(offset).as_ptr(),
+                    self.ptr.offset(offset).as_ptr(),
                     self.len(),
                 ))
             }
@@ -1580,101 +1577,6 @@ where
         }
     }
 
-    /// Transform the array into `new_shape`; any shape with the same number of elements is
-    /// accepted.
-    ///
-    /// `order` specifies the *logical* order in which the array is to be read and reshaped.
-    /// The array is returned as a `CowArray`; a view if possible, otherwise an owned array.
-    ///
-    /// For example, when starting from the one-dimensional sequence 1 2 3 4 5 6, it would be
-    /// understood as a 2 x 3 array in row major ("C") order this way:
-    ///
-    /// ```text
-    /// 1 2 3
-    /// 4 5 6
-    /// ```
-    ///
-    /// and as 2 x 3 in column major ("F") order this way:
-    ///
-    /// ```text
-    /// 1 3 5
-    /// 2 4 6
-    /// ```
-    ///
-    /// This example should show that any time we "reflow" the elements in the array to a different
-    /// number of rows and columns (or more axes if applicable), it is important to pick an index
-    /// ordering, and that's the reason for the function parameter for `order`.
-    ///
-    /// **Errors** if the new shape doesn't have the same number of elements as the array's current
-    /// shape.
-    ///
-    /// ```
-    /// use ndarray::array;
-    /// use ndarray::Order;
-    ///
-    /// assert!(
-    ///     array![1., 2., 3., 4., 5., 6.].to_shape(((2, 3), Order::RowMajor)).unwrap()
-    ///     == array![[1., 2., 3.],
-    ///               [4., 5., 6.]]
-    /// );
-    ///
-    /// assert!(
-    ///     array![1., 2., 3., 4., 5., 6.].to_shape(((2, 3), Order::ColumnMajor)).unwrap()
-    ///     == array![[1., 3., 5.],
-    ///               [2., 4., 6.]]
-    /// );
-    /// ```
-    pub fn to_shape<E>(&self, new_shape: E) -> Result<CowArray<'_, A, E::Dim>, ShapeError>
-    where
-        E: ShapeArg,
-        A: Clone,
-        S: Data,
-    {
-        let (shape, order) = new_shape.into_shape_and_order();
-        self.to_shape_order(shape, order.unwrap_or(Order::RowMajor))
-    }
-
-    fn to_shape_order<E>(&self, shape: E, order: Order)
-        -> Result<CowArray<'_, A, E>, ShapeError>
-    where
-        E: Dimension,
-        A: Clone,
-        S: Data,
-    {
-        let len = self.dim.size();
-        if size_of_shape_checked(&shape) != Ok(len) {
-            return Err(error::incompatible_shapes(&self.dim, &shape));
-        }
-
-        // Create a view if the length is 0, safe because the array and new shape is empty.
-        if len == 0 {
-            unsafe {
-                return Ok(CowArray::from(ArrayView::from_shape_ptr(shape, self.as_ptr())));
-            }
-        }
-
-        // Try to reshape the array as a view into the existing data
-        match reshape_dim(&self.dim, &self.strides, &shape, order) {
-            Ok(to_strides) => unsafe {
-                return Ok(CowArray::from(ArrayView::new(self.ptr, shape, to_strides)));
-            }
-            Err(err) if err.kind() == ErrorKind::IncompatibleShape => {
-                return Err(error::incompatible_shapes(&self.dim, &shape));
-            }
-            _otherwise => { }
-        }
-
-        // otherwise create a new array and copy the elements
-        unsafe {
-            let (shape, view) = match order {
-                Order::RowMajor => (shape.set_f(false), self.view()),
-                Order::ColumnMajor => (shape.set_f(true), self.t()),
-            };
-            Ok(CowArray::from(Array::from_shape_trusted_iter_unchecked(
-                        shape, view.into_iter(), A::clone)))
-        }
-    }
-
     /// Transform the array into `shape`; any shape with the same number of
     /// elements is accepted, but the source array or view must be in standard
     /// or column-major (Fortran) layout.
diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs
index 63af6391..d148a115 100644
--- a/src/impl_owned_array.rs
+++ b/src/impl_owned_array.rs
@@ -1,5 +1,6 @@
 
 use alloc::vec::Vec;
+use std::mem;
 use std::mem::MaybeUninit;
 
 use rawpointer::PointerExt;
@@ -33,7 +34,7 @@ impl<A> Array<A, Ix0> {
     /// assert_eq!(scalar, Foo);
     /// ```
     pub fn into_scalar(self) -> A {
-        let size = ::std::mem::size_of::<A>();
+        let size = 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)
@@ -155,6 +156,51 @@ 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.
     ///
@@ -167,12 +213,26 @@ 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 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)
     where
         AM: Into<ArrayViewMut<'a, MaybeUninit<A>, D>>,
         A: 'a,
     {
-        // Remove generic parameter P and call the implementation
+        // Remove generic parameter AM and call the implementation
         self.move_into_impl(new_array.into())
     }
 
@@ -180,7 +240,8 @@ 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
+            // 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)`.
             Zip::from(self.raw_view_mut())
                 .and(new_array)
                 .for_each(|src, dst| {
@@ -208,7 +269,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 || std::mem::size_of::<A>() == 0 {
+        if !has_unreachable_elements || mem::size_of::<A>() == 0 || !mem::needs_drop::<A>() {
             unsafe {
                 self.data.set_len(0);
             }
@@ -270,7 +331,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 {
diff --git a/src/impl_views/constructors.rs b/src/impl_views/constructors.rs
index 98cb81fc..fd9457fa 100644
--- a/src/impl_views/constructors.rs
+++ b/src/impl_views/constructors.rs
@@ -13,7 +13,7 @@ use crate::error::ShapeError;
 use crate::extension::nonnull::nonnull_debug_checked_from_ptr;
 use crate::imp_prelude::*;
 use crate::{is_aligned, StrideShape};
-use crate::dimension::offset_from_low_addr_ptr_to_logical_ptr;
+use crate::dimension::offset_from_ptr_to_memory;
 
 /// Methods for read-only array views.
 impl<'a, A, D> ArrayView<'a, A, D>
@@ -57,7 +57,7 @@ where
         let dim = shape.dim;
         dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?;
         let strides = shape.strides.strides_for_dim(&dim);
-        unsafe { Ok(Self::new_(xs.as_ptr().add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), dim, strides)) }
+        unsafe { Ok(Self::new_(xs.as_ptr().offset(-offset_from_ptr_to_memory(&dim, &strides)), dim, strides)) }
     }
 
     /// Create an `ArrayView<A, D>` from shape information and a raw pointer to
@@ -154,7 +154,7 @@ where
         let dim = shape.dim;
         dimension::can_index_slice_with_strides(xs, &dim, &shape.strides)?;
         let strides = shape.strides.strides_for_dim(&dim);
-        unsafe { Ok(Self::new_(xs.as_mut_ptr().add(offset_from_low_addr_ptr_to_logical_ptr(&dim, &strides)), dim, strides)) }
+        unsafe { Ok(Self::new_(xs.as_mut_ptr().offset(-offset_from_ptr_to_memory(&dim, &strides)), dim, strides)) }
     }
 
     /// Create an `ArrayViewMut<A, D>` from shape information and a
diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs
index cfd7f9aa..5dba16d9 100644
--- a/src/impl_views/conversions.rs
+++ b/src/impl_views/conversions.rs
@@ -7,6 +7,7 @@
 // except according to those terms.
 
 use alloc::slice;
+use std::mem::MaybeUninit;
 
 use crate::imp_prelude::*;
 
@@ -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
diff --git a/src/lib.rs b/src/lib.rs
index dfce924e..a4c59eb6 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -145,7 +145,6 @@ pub use crate::dimension::IxDynImpl;
 pub use crate::dimension::NdIndex;
 pub use crate::error::{ErrorKind, ShapeError};
 pub use crate::indexes::{indices, indices_of};
-pub use crate::order::Order;
 pub use crate::slice::{
     MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceInfoElem, SliceNextDim,
 };
@@ -163,7 +162,7 @@ pub use crate::stacking::{concatenate, stack, stack_new_axis};
 
 pub use crate::math_cell::MathCell;
 pub use crate::impl_views::IndexLonger;
-pub use crate::shape_builder::{Shape, ShapeBuilder, ShapeArg, StrideShape};
+pub use crate::shape_builder::{Shape, ShapeBuilder, StrideShape};
 
 #[macro_use]
 mod macro_utils;
@@ -203,7 +202,6 @@ mod linspace;
 mod logspace;
 mod math_cell;
 mod numeric_util;
-mod order;
 mod partial;
 mod shape_builder;
 #[macro_use]
diff --git a/src/order.rs b/src/order.rs
deleted file mode 100644
index e8d9c8db..00000000
--- a/src/order.rs
+++ /dev/null
@@ -1,83 +0,0 @@
-
-/// Array order
-///
-/// Order refers to indexing order, or how a linear sequence is translated
-/// into a two-dimensional or multi-dimensional array.
-///
-/// - `RowMajor` means that the index along the row is the most rapidly changing
-/// - `ColumnMajor` means that the index along the column is the most rapidly changing
-///
-/// Given a sequence like: 1, 2, 3, 4, 5, 6
-///
-/// If it is laid it out in a 2 x 3 matrix using row major ordering, it results in:
-///
-/// ```text
-/// 1  2  3
-/// 4  5  6
-/// ```
-///
-/// If it is laid using column major ordering, it results in:
-///
-/// ```text
-/// 1  3  5
-/// 2  4  6
-/// ```
-///
-/// It can be seen as filling in "rows first" or "columns first".
-///
-/// `Order` can be used both to refer to logical ordering as well as memory ordering or memory
-/// layout. The orderings have common short names, also seen in other environments, where
-/// row major is called "C" order (after the C programming language) and column major is called "F"
-/// or "Fortran" order.
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-#[non_exhaustive]
-pub enum Order {
-    /// Row major or "C" order
-    RowMajor,
-    /// Column major or "F" order
-    ColumnMajor,
-}
-
-impl Order {
-    /// "C" is an alias for row major ordering
-    pub const C: Order = Order::RowMajor;
-
-    /// "F" (for Fortran) is an alias for column major ordering
-    pub const F: Order = Order::ColumnMajor;
-
-    /// Return true if input is Order::RowMajor, false otherwise
-    #[inline]
-    pub fn is_row_major(self) -> bool {
-        match self {
-            Order::RowMajor => true,
-            Order::ColumnMajor => false,
-        }
-    }
-
-    /// Return true if input is Order::ColumnMajor, false otherwise
-    #[inline]
-    pub fn is_column_major(self) -> bool {
-        !self.is_row_major()
-    }
-
-    /// Return Order::RowMajor if the input is true, Order::ColumnMajor otherwise
-    #[inline]
-    pub fn row_major(row_major: bool) -> Order {
-        if row_major { Order::RowMajor } else { Order::ColumnMajor }
-    }
-
-    /// Return Order::ColumnMajor if the input is true, Order::RowMajor otherwise
-    #[inline]
-    pub fn column_major(column_major: bool) -> Order {
-        Self::row_major(!column_major)
-    }
-
-    /// Return the transpose: row major becomes column major and vice versa.
-    #[inline]
-    pub fn transpose(self) -> Order {
-        match self {
-            Order::RowMajor => Order::ColumnMajor,
-            Order::ColumnMajor => Order::RowMajor,
-        }
-    }
-}
diff --git a/src/shape_builder.rs b/src/shape_builder.rs
index 47037407..dcfddc1b 100644
--- a/src/shape_builder.rs
+++ b/src/shape_builder.rs
@@ -1,6 +1,5 @@
 use crate::dimension::IntoDimension;
 use crate::Dimension;
-use crate::order::Order;
 
 /// A contiguous array shape of n dimensions.
 ///
@@ -185,33 +184,3 @@ where
         self.dim.size()
     }
 }
-
-
-/// Array shape argument with optional order parameter
-///
-/// Shape or array dimension argument, with optional [`Order`] parameter.
-///
-/// This is an argument conversion trait that is used to accept an array shape and
-/// (optionally) an ordering argument.
-///
-/// See for example [`.to_shape()`](crate::ArrayBase::to_shape).
-pub trait ShapeArg {
-    type Dim: Dimension;
-    fn into_shape_and_order(self) -> (Self::Dim, Option<Order>);
-}
-
-impl<T> ShapeArg for T where T: IntoDimension {
-    type Dim = T::Dim;
-
-    fn into_shape_and_order(self) -> (Self::Dim, Option<Order>) {
-        (self.into_dimension(), None)
-    }
-}
-
-impl<T> ShapeArg for (T, Order) where T: IntoDimension {
-    type Dim = T::Dim;
-
-    fn into_shape_and_order(self) -> (Self::Dim, Option<Order>) {
-        (self.0.into_dimension(), Some(self.1))
-    }
-}
diff --git a/src/zip/mod.rs b/src/zip/mod.rs
index 7277f99f..07fcbb06 100644
--- a/src/zip/mod.rs
+++ b/src/zip/mod.rs
@@ -745,7 +745,7 @@ macro_rules! map_impl {
                     // Use partial to count the number of filled elements, and can drop the right
                     // number of elements on unwinding (if it happens during apply/collect).
                     unsafe {
-                        let output_view = output.into_raw_view_mut().cast::<R>();
+                        let output_view = output.cast::<R>();
                         self.and(output_view)
                             .collect_with_partial(f)
                             .release_ownership();


Test patch:
diff --git a/tests/array-construct.rs b/tests/array-construct.rs
index c8948d1a..2b72ab03 100644
--- a/tests/array-construct.rs
+++ b/tests/array-construct.rs
@@ -7,7 +7,6 @@
 
 use defmac::defmac;
 use ndarray::prelude::*;
-use ndarray::arr3;
 use ndarray::Zip;
 
 #[test]
@@ -165,43 +164,6 @@ fn test_ones() {
     assert_eq!(a, b);
 }
 
-#[test]
-fn test_from_shape_empty_with_neg_stride() {
-    // Issue #998, negative strides for an axis where it doesn't matter.
-    let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
-    let v = s[..12].to_vec();
-    let v_ptr = v.as_ptr();
-    let a = Array::from_shape_vec((2, 0, 2).strides((1, -4isize as usize, 2)), v).unwrap();
-    assert_eq!(a, arr3(&[[[0; 2]; 0]; 2]));
-    assert_eq!(a.as_ptr(), v_ptr);
-}
-
-#[test]
-fn test_from_shape_with_neg_stride() {
-    // Issue #998, negative strides for an axis where it doesn't matter.
-    let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
-    let v = s[..12].to_vec();
-    let v_ptr = v.as_ptr();
-    let a = Array::from_shape_vec((2, 1, 2).strides((1, -4isize as usize, 2)), v).unwrap();
-    assert_eq!(a, arr3(&[[[0, 2]],
-                         [[1, 3]]]));
-    assert_eq!(a.as_ptr(), v_ptr);
-}
-
-#[test]
-fn test_from_shape_2_2_2_with_neg_stride() {
-    // Issue #998, negative strides for an axis where it doesn't matter.
-    let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
-    let v = s[..12].to_vec();
-    let v_ptr = v.as_ptr();
-    let a = Array::from_shape_vec((2, 2, 2).strides((1, -4isize as usize, 2)), v).unwrap();
-    assert_eq!(a, arr3(&[[[4, 6],
-                          [0, 2]],
-                         [[5, 7],
-                          [1, 3]]]));
-    assert_eq!(a.as_ptr(), v_ptr.wrapping_add(4));
-}
-
 #[should_panic]
 #[test]
 fn deny_wraparound_zeros() {
diff --git a/tests/array.rs b/tests/array.rs
index edd58adb..976824df 100644
--- a/tests/array.rs
+++ b/tests/array.rs
@@ -8,7 +8,7 @@
 )]
 
 use defmac::defmac;
-use itertools::{zip, Itertools};
+use itertools::{enumerate, zip, Itertools};
 use ndarray::prelude::*;
 use ndarray::{arr3, rcarr2};
 use ndarray::indices;
@@ -1370,6 +1370,64 @@ fn transpose_view_mut() {
     assert_eq!(at, arr2(&[[1, 4], [2, 5], [3, 7]]));
 }
 
+#[test]
+fn reshape() {
+    let data = [1, 2, 3, 4, 5, 6, 7, 8];
+    let v = aview1(&data);
+    let u = v.into_shape((3, 3));
+    assert!(u.is_err());
+    let u = v.into_shape((2, 2, 2));
+    assert!(u.is_ok());
+    let u = u.unwrap();
+    assert_eq!(u.shape(), &[2, 2, 2]);
+    let s = u.into_shape((4, 2)).unwrap();
+    assert_eq!(s.shape(), &[4, 2]);
+    assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]]));
+}
+
+#[test]
+#[should_panic(expected = "IncompatibleShape")]
+fn reshape_error1() {
+    let data = [1, 2, 3, 4, 5, 6, 7, 8];
+    let v = aview1(&data);
+    let _u = v.into_shape((2, 5)).unwrap();
+}
+
+#[test]
+#[should_panic(expected = "IncompatibleLayout")]
+fn reshape_error2() {
+    let data = [1, 2, 3, 4, 5, 6, 7, 8];
+    let v = aview1(&data);
+    let mut u = v.into_shape((2, 2, 2)).unwrap();
+    u.swap_axes(0, 1);
+    let _s = u.into_shape((2, 4)).unwrap();
+}
+
+#[test]
+fn reshape_f() {
+    let mut u = Array::zeros((3, 4).f());
+    for (i, elt) in enumerate(u.as_slice_memory_order_mut().unwrap()) {
+        *elt = i as i32;
+    }
+    let v = u.view();
+    println!("{:?}", v);
+
+    // noop ok
+    let v2 = v.into_shape((3, 4));
+    assert!(v2.is_ok());
+    assert_eq!(v, v2.unwrap());
+
+    let u = v.into_shape((3, 2, 2));
+    assert!(u.is_ok());
+    let u = u.unwrap();
+    println!("{:?}", u);
+    assert_eq!(u.shape(), &[3, 2, 2]);
+    let s = u.into_shape((4, 3)).unwrap();
+    println!("{:?}", s);
+    assert_eq!(s.shape(), &[4, 3]);
+    assert_eq!(s, aview2(&[[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]]));
+}
+
 #[test]
 #[allow(clippy::cognitive_complexity)]
 fn insert_axis() {
diff --git a/tests/assign.rs b/tests/assign.rs
index 19156cce..5c300943 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(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);
 }
@@ -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);
@@ -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();
@@ -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;
@@ -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();
         {
@@ -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;
@@ -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();
         {
@@ -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;
@@ -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();
         {
@@ -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;
@@ -194,6 +194,35 @@ 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.
diff --git a/tests/reshape.rs b/tests/reshape.rs
deleted file mode 100644
index 21fe407e..00000000
--- a/tests/reshape.rs
+++ /dev/null
@@ -1,232 +0,0 @@
-use ndarray::prelude::*;
-
-use itertools::enumerate;
-
-use ndarray::Order;
-
-#[test]
-fn reshape() {
-    let data = [1, 2, 3, 4, 5, 6, 7, 8];
-    let v = aview1(&data);
-    let u = v.into_shape((3, 3));
-    assert!(u.is_err());
-    let u = v.into_shape((2, 2, 2));
-    assert!(u.is_ok());
-    let u = u.unwrap();
-    assert_eq!(u.shape(), &[2, 2, 2]);
-    let s = u.into_shape((4, 2)).unwrap();
-    assert_eq!(s.shape(), &[4, 2]);
-    assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]]));
-}
-
-#[test]
-#[should_panic(expected = "IncompatibleShape")]
-fn reshape_error1() {
-    let data = [1, 2, 3, 4, 5, 6, 7, 8];
-    let v = aview1(&data);
-    let _u = v.into_shape((2, 5)).unwrap();
-}
-
-#[test]
-#[should_panic(expected = "IncompatibleLayout")]
-fn reshape_error2() {
-    let data = [1, 2, 3, 4, 5, 6, 7, 8];
-    let v = aview1(&data);
-    let mut u = v.into_shape((2, 2, 2)).unwrap();
-    u.swap_axes(0, 1);
-    let _s = u.into_shape((2, 4)).unwrap();
-}
-
-#[test]
-fn reshape_f() {
-    let mut u = Array::zeros((3, 4).f());
-    for (i, elt) in enumerate(u.as_slice_memory_order_mut().unwrap()) {
-        *elt = i as i32;
-    }
-    let v = u.view();
-    println!("{:?}", v);
-
-    // noop ok
-    let v2 = v.into_shape((3, 4));
-    assert!(v2.is_ok());
-    assert_eq!(v, v2.unwrap());
-
-    let u = v.into_shape((3, 2, 2));
-    assert!(u.is_ok());
-    let u = u.unwrap();
-    println!("{:?}", u);
-    assert_eq!(u.shape(), &[3, 2, 2]);
-    let s = u.into_shape((4, 3)).unwrap();
-    println!("{:?}", s);
-    assert_eq!(s.shape(), &[4, 3]);
-    assert_eq!(s, aview2(&[[0, 4, 8], [1, 5, 9], [2, 6, 10], [3, 7, 11]]));
-}
-
-
-#[test]
-fn to_shape_easy() {
-    // 1D -> C -> C
-    let data = [1, 2, 3, 4, 5, 6, 7, 8];
-    let v = aview1(&data);
-    let u = v.to_shape(((3, 3), Order::RowMajor));
-    assert!(u.is_err());
-
-    let u = v.to_shape(((2, 2, 2), Order::C));
-    assert!(u.is_ok());
-
-    let u = u.unwrap();
-    assert!(u.is_view());
-    assert_eq!(u.shape(), &[2, 2, 2]);
-    assert_eq!(u, array![[[1, 2], [3, 4]], [[5, 6], [7, 8]]]);
-
-    let s = u.to_shape((4, 2)).unwrap();
-    assert_eq!(s.shape(), &[4, 2]);
-    assert_eq!(s, aview2(&[[1, 2], [3, 4], [5, 6], [7, 8]]));
-
-    // 1D -> F -> F
-    let data = [1, 2, 3, 4, 5, 6, 7, 8];
-    let v = aview1(&data);
-    let u = v.to_shape(((3, 3), Order::ColumnMajor));
-    assert!(u.is_err());
-
-    let u = v.to_shape(((2, 2, 2), Order::ColumnMajor));
-    assert!(u.is_ok());
-
-    let u = u.unwrap();
-    assert!(u.is_view());
-    assert_eq!(u.shape(), &[2, 2, 2]);
-    assert_eq!(u, array![[[1, 5], [3, 7]], [[2, 6], [4, 8]]]);
-
-    let s = u.to_shape(((4, 2), Order::ColumnMajor)).unwrap();
-    assert_eq!(s.shape(), &[4, 2]);
-    assert_eq!(s, array![[1, 5], [2, 6], [3, 7], [4, 8]]);
-}
-
-#[test]
-fn to_shape_copy() {
-    // 1D -> C -> F
-    let v = ArrayView::from(&[1, 2, 3, 4, 5, 6, 7, 8]);
-    let u = v.to_shape(((4, 2), Order::RowMajor)).unwrap();
-    assert_eq!(u.shape(), &[4, 2]);
-    assert_eq!(u, array![[1, 2], [3, 4], [5, 6], [7, 8]]);
-
-    let u = u.to_shape(((2, 4), Order::ColumnMajor)).unwrap();
-    assert_eq!(u.shape(), &[2, 4]);
-    assert_eq!(u, array![[1, 5, 2, 6], [3, 7, 4, 8]]);
-
-    // 1D -> F -> C
-    let v = ArrayView::from(&[1, 2, 3, 4, 5, 6, 7, 8]);
-    let u = v.to_shape(((4, 2), Order::ColumnMajor)).unwrap();
-    assert_eq!(u.shape(), &[4, 2]);
-    assert_eq!(u, array![[1, 5], [2, 6], [3, 7], [4, 8]]);
-
-    let u = u.to_shape((2, 4)).unwrap();
-    assert_eq!(u.shape(), &[2, 4]);
-    assert_eq!(u, array![[1, 5, 2, 6], [3, 7, 4, 8]]);
-}
-
-#[test]
-fn to_shape_add_axis() {
-    // 1D -> C -> C
-    let data = [1, 2, 3, 4, 5, 6, 7, 8];
-    let v = aview1(&data);
-    let u = v.to_shape(((4, 2), Order::RowMajor)).unwrap();
-
-    assert!(u.to_shape(((1, 4, 2), Order::RowMajor)).unwrap().is_view());
-    assert!(u.to_shape(((1, 4, 2), Order::ColumnMajor)).unwrap().is_view());
-}
-
-
-#[test]
-fn to_shape_copy_stride() {
-    let v = array![[1, 2, 3, 4], [5, 6, 7, 8]];
-    let vs = v.slice(s![.., ..3]);
-    let lin1 = vs.to_shape(6).unwrap();
-    assert_eq!(lin1, array![1, 2, 3, 5, 6, 7]);
-    assert!(lin1.is_owned());
-
-    let lin2 = vs.to_shape((6, Order::ColumnMajor)).unwrap();
-    assert_eq!(lin2, array![1, 5, 2, 6, 3, 7]);
-    assert!(lin2.is_owned());
-}
-
-
-#[test]
-fn to_shape_zero_len() {
-    let v = array![[1, 2, 3, 4], [5, 6, 7, 8]];
-    let vs = v.slice(s![.., ..0]);
-    let lin1 = vs.to_shape(0).unwrap();
-    assert_eq!(lin1, array![]);
-    assert!(lin1.is_view());
-}
-
-#[test]
-#[should_panic(expected = "IncompatibleShape")]
-fn to_shape_error1() {
-    let data = [1, 2, 3, 4, 5, 6, 7, 8];
-    let v = aview1(&data);
-    let _u = v.to_shape((2, 5)).unwrap();
-}
-
-#[test]
-#[should_panic(expected = "IncompatibleShape")]
-fn to_shape_error2() {
-    // overflow
-    let data = [3, 4, 5, 6, 7, 8];
-    let v = aview1(&data);
-    let _u = v.to_shape((2, usize::MAX)).unwrap();
-}
-
-#[test]
-fn to_shape_discontig() {
-    for &create_order in &[Order::C, Order::F] {
-        let a = Array::from_iter(0..64);
-        let mut a1 = a.to_shape(((4, 4, 4), create_order)).unwrap();
-        a1.slice_collapse(s![.., ..;2, ..]); // now shape (4, 2, 4)
-        assert!(a1.as_slice_memory_order().is_none());
-
-        for &order in &[Order::C, Order::F] {
-            let v1 = a1.to_shape(((2, 2, 2, 2, 2), order)).unwrap();
-            assert!(v1.is_view());
-            let v1 = a1.to_shape(((4, 1, 2, 1, 2, 2), order)).unwrap();
-            assert!(v1.is_view());
-            let v1 = a1.to_shape(((4, 2, 4), order)).unwrap();
-            assert!(v1.is_view());
-            let v1 = a1.to_shape(((8, 4), order)).unwrap();
-            assert_eq!(v1.is_view(), order == create_order && create_order == Order::C,
-                       "failed for {:?}, {:?}", create_order, order);
-            let v1 = a1.to_shape(((4, 8), order)).unwrap();
-            assert_eq!(v1.is_view(), order == create_order && create_order == Order::F,
-                       "failed for {:?}, {:?}", create_order, order);
-            let v1 = a1.to_shape((32, order)).unwrap();
-            assert!(!v1.is_view());
-        }
-    }
-}
-
-#[test]
-fn to_shape_broadcast() {
-    for &create_order in &[Order::C, Order::F] {
-        let a = Array::from_iter(0..64);
-        let mut a1 = a.to_shape(((4, 4, 4), create_order)).unwrap();
-        a1.slice_collapse(s![.., ..1, ..]); // now shape (4, 1, 4)
-        let v1 = a1.broadcast((4, 4, 4)).unwrap(); // Now shape (4, 4, 4)
-        assert!(v1.as_slice_memory_order().is_none());
-
-        for &order in &[Order::C, Order::F] {
-            let v2 = v1.to_shape(((2, 2, 2, 2, 2, 2), order)).unwrap();
-            assert_eq!(v2.strides(), match (create_order, order) {
-                (Order::C, Order::C) => { &[32, 16, 0, 0, 2, 1] }
-                (Order::C, Order::F) => { &[16, 32, 0, 0, 1, 2] }
-                (Order::F, Order::C) => { &[2, 1, 0, 0, 32, 16] }
-                (Order::F, Order::F) => { &[1, 2, 0, 0, 16, 32] }
-                _other => unreachable!()
-            });
-
-            let v2 = v1.to_shape(((4, 4, 4), order)).unwrap();
-            assert!(v2.is_view());
-            let v2 = v1.to_shape(((8, 8), order)).unwrap();
-            assert!(v2.is_owned());
-        }
-    }
-}