Repository: rust-ndarray/ndarray
Pull request #947 (2021-03-17 22:02:33)

Add new method .assign_to()
This method does the same job as Zip::from(a).map_assign_into(f, b) with
cloning as the function f.

Comparison with existing assign:

- Reverse argument order: source, destination
- The new method doesn't broadcast.
- The new method is generic over AssignElem (assign into &Cell, &mut
ManuallyUninit etc).


bluss: Unsure if these methods pull their weight (they add nothing new, but are expressive because they are easy to use). Their properties contrast with the existing `.assign()`.

Best we can do for existing .assign is to generalize it so that it can assign to both "T" and "ManuallyUninit<T>" arrays.  The assign_into solution additionally handles "Cell<T>" arrays.

Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 22686c5b..301d1b56 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -73,8 +73,6 @@ docs = ["approx", "serde", "rayon"]
 std = ["num-traits/std", "matrixmultiply/std"]
 rayon = ["rayon_", "std"]
 
-matrixmultiply-threading = ["matrixmultiply/threading"]
-
 [profile.release]
 [profile.bench]
 debug = true
diff --git a/README.rst b/README.rst
index 36b98598..8884ac9f 100644
--- a/README.rst
+++ b/README.rst
@@ -86,10 +86,6 @@ your `Cargo.toml`.
     Uses ``blas-src`` for pluggable backend, which needs to be configured
     separately (see below).
 
-- ``matrixmultiply-threading``
-
-  - Enable the ``threading`` feature in the matrixmultiply package
-
 How to use with cargo
 ---------------------
 
diff --git a/src/dimension/mod.rs b/src/dimension/mod.rs
index fb062513..243d5eee 100644
--- a/src/dimension/mod.rs
+++ b/src/dimension/mod.rs
@@ -8,7 +8,7 @@
 
 use crate::error::{from_kind, ErrorKind, ShapeError};
 use crate::slice::SliceArg;
-use crate::{Ix, Ixs, Slice, SliceInfoElem};
+use crate::{AxisSliceInfo, Ix, Ixs, Slice};
 use num_integer::div_floor;
 
 pub use self::axes::{axes_of, Axes, AxisDescription};
@@ -599,8 +599,8 @@ fn slice_min_max(axis_len: usize, slice: Slice) -> Option<(usize, usize)> {
 /// Returns `true` iff the slices intersect.
 pub fn slices_intersect<D: Dimension>(
     dim: &D,
-    indices1: impl SliceArg<D>,
-    indices2: impl SliceArg<D>,
+    indices1: &impl SliceArg<D>,
+    indices2: &impl SliceArg<D>,
 ) -> bool {
     debug_assert_eq!(indices1.in_ndim(), indices2.in_ndim());
     for (&axis_len, &si1, &si2) in izip!(
@@ -608,15 +608,15 @@ pub fn slices_intersect<D: Dimension>(
         indices1.as_ref().iter().filter(|si| !si.is_new_axis()),
         indices2.as_ref().iter().filter(|si| !si.is_new_axis()),
     ) {
-        // The slices do not intersect iff any pair of `SliceInfoElem` does not intersect.
+        // The slices do not intersect iff any pair of `AxisSliceInfo` does not intersect.
         match (si1, si2) {
             (
-                SliceInfoElem::Slice {
+                AxisSliceInfo::Slice {
                     start: start1,
                     end: end1,
                     step: step1,
                 },
-                SliceInfoElem::Slice {
+                AxisSliceInfo::Slice {
                     start: start2,
                     end: end2,
                     step: step2,
@@ -637,8 +637,8 @@ pub fn slices_intersect<D: Dimension>(
                     return false;
                 }
             }
-            (SliceInfoElem::Slice { start, end, step }, SliceInfoElem::Index(ind))
-            | (SliceInfoElem::Index(ind), SliceInfoElem::Slice { start, end, step }) => {
+            (AxisSliceInfo::Slice { start, end, step }, AxisSliceInfo::Index(ind))
+            | (AxisSliceInfo::Index(ind), AxisSliceInfo::Slice { start, end, step }) => {
                 let ind = abs_index(axis_len, ind);
                 let (min, max) = match slice_min_max(axis_len, Slice::new(start, end, step)) {
                     Some(m) => m,
@@ -648,14 +648,14 @@ pub fn slices_intersect<D: Dimension>(
                     return false;
                 }
             }
-            (SliceInfoElem::Index(ind1), SliceInfoElem::Index(ind2)) => {
+            (AxisSliceInfo::Index(ind1), AxisSliceInfo::Index(ind2)) => {
                 let ind1 = abs_index(axis_len, ind1);
                 let ind2 = abs_index(axis_len, ind2);
                 if ind1 != ind2 {
                     return false;
                 }
             }
-            (SliceInfoElem::NewAxis, _) | (_, SliceInfoElem::NewAxis) => unreachable!(),
+            (AxisSliceInfo::NewAxis, _) | (_, AxisSliceInfo::NewAxis) => unreachable!(),
         }
     }
     true
diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs
index 63688ea7..d082a5ce 100644
--- a/src/impl_constructors.rs
+++ b/src/impl_constructors.rs
@@ -531,30 +531,14 @@ where
     ///     // two first columns in b are two last in a
     ///     // rest of columns in b are the initial columns in a
     ///
-    ///     assign_to(a.slice(s![.., -2..]), b.slice_mut(s![.., ..2]));
-    ///     assign_to(a.slice(s![.., 2..]), b.slice_mut(s![.., ..-2]));
+    ///     a.slice(s![.., -2..]).assign_to(b.slice_mut(s![.., ..2]));
+    ///     a.slice(s![.., 2..]).assign_to(b.slice_mut(s![.., ..-2]));
     ///
     ///     // Now we can promise that `b` is safe to use with all operations
     ///     unsafe {
     ///         b.assume_init()
     ///     }
     /// }
-    ///
-    /// use ndarray::{IntoNdProducer, AssignElem};
-    ///
-    /// // This function clones elements from the first input to the second;
-    /// // the two producers must have the same shape
-    /// fn assign_to<'a, P1, P2, A>(from: P1, to: P2)
-    ///     where P1: IntoNdProducer<Item = &'a A>,
-    ///           P2: IntoNdProducer<Dim = P1::Dim>,
-    ///           P2::Item: AssignElem<A>,
-    ///           A: Clone + 'a
-    /// {
-    ///     Zip::from(from)
-    ///         .map_assign_into(to, A::clone);
-    /// }
-    ///
-    /// # shift_by_two(&Array2::zeros((8, 8)));
     /// ```
     pub fn uninit<Sh>(shape: Sh) -> ArrayBase<S::MaybeUninit, D>
     where
diff --git a/src/impl_methods.rs b/src/impl_methods.rs
index 4696fccb..5b17c817 100644
--- a/src/impl_methods.rs
+++ b/src/impl_methods.rs
@@ -15,6 +15,7 @@ use rawpointer::PointerExt;
 use crate::imp_prelude::*;
 
 use crate::{arraytraits, DimMax};
+use crate::argument_traits::AssignElem;
 use crate::dimension;
 use crate::dimension::IntoDimension;
 use crate::dimension::{
@@ -25,7 +26,7 @@ use crate::dimension::broadcast::co_broadcast;
 use crate::error::{self, ErrorKind, ShapeError, from_kind};
 use crate::math_cell::MathCell;
 use crate::itertools::zip;
-use crate::zip::Zip;
+use crate::zip::{IntoNdProducer, Zip};
 use crate::AxisDescription;
 
 use crate::iter::{
@@ -34,7 +35,7 @@ use crate::iter::{
 };
 use crate::slice::{MultiSliceArg, SliceArg};
 use crate::stacking::concatenate;
-use crate::{NdIndex, Slice, SliceInfoElem};
+use crate::{AxisSliceInfo, NdIndex, Slice};
 
 /// # Methods For All Array Types
 impl<A, S, D> ArrayBase<S, D>
@@ -338,9 +339,9 @@ where
     ///
     /// **Panics** if an index is out of bounds or step size is zero.<br>
     /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.)
-    pub fn slice<I>(&self, info: I) -> ArrayView<'_, A, I::OutDim>
+    pub fn slice<I>(&self, info: &I) -> ArrayView<'_, A, I::OutDim>
     where
-        I: SliceArg<D>,
+        I: SliceArg<D> + ?Sized,
         S: Data,
     {
         self.view().slice_move(info)
@@ -353,9 +354,9 @@ where
     ///
     /// **Panics** if an index is out of bounds or step size is zero.<br>
     /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.)
-    pub fn slice_mut<I>(&mut self, info: I) -> ArrayViewMut<'_, A, I::OutDim>
+    pub fn slice_mut<I>(&mut self, info: &I) -> ArrayViewMut<'_, A, I::OutDim>
     where
-        I: SliceArg<D>,
+        I: SliceArg<D> + ?Sized,
         S: DataMut,
     {
         self.view_mut().slice_move(info)
@@ -399,9 +400,9 @@ where
     ///
     /// **Panics** if an index is out of bounds or step size is zero.<br>
     /// (**Panics** if `D` is `IxDyn` and `info` does not match the number of array axes.)
-    pub fn slice_move<I>(mut self, info: I) -> ArrayBase<S, I::OutDim>
+    pub fn slice_move<I>(mut self, info: &I) -> ArrayBase<S, I::OutDim>
     where
-        I: SliceArg<D>,
+        I: SliceArg<D> + ?Sized,
     {
         assert_eq!(
             info.in_ndim(),
@@ -415,7 +416,7 @@ where
         let mut old_axis = 0;
         let mut new_axis = 0;
         info.as_ref().iter().for_each(|&ax_info| match ax_info {
-            SliceInfoElem::Slice { start, end, step } => {
+            AxisSliceInfo::Slice { start, end, step } => {
                 // Slice the axis in-place to update the `dim`, `strides`, and `ptr`.
                 self.slice_axis_inplace(Axis(old_axis), Slice { start, end, step });
                 // Copy the sliced dim and stride to corresponding axis.
@@ -424,7 +425,7 @@ where
                 old_axis += 1;
                 new_axis += 1;
             }
-            SliceInfoElem::Index(index) => {
+            AxisSliceInfo::Index(index) => {
                 // Collapse the axis in-place to update the `ptr`.
                 let i_usize = abs_index(self.len_of(Axis(old_axis)), index);
                 self.collapse_axis(Axis(old_axis), i_usize);
@@ -434,7 +435,7 @@ where
                 // is zero length.
                 old_axis += 1;
             }
-            SliceInfoElem::NewAxis => {
+            AxisSliceInfo::NewAxis => {
                 // Set the dim and stride of the new axis.
                 new_dim[new_axis] = 1;
                 new_strides[new_axis] = 0;
@@ -465,12 +466,12 @@ where
     ///
     /// - if an index is out of bounds
     /// - if a step size is zero
-    /// - if [`SliceInfoElem::NewAxis`] is in `info`, e.g. if [`NewAxis`] was
+    /// - if [`AxisSliceInfo::NewAxis`] is in `info`, e.g. if [`NewAxis`] was
     ///   used in the [`s!`] macro
     /// - if `D` is `IxDyn` and `info` does not match the number of array axes
-    pub fn slice_collapse<I>(&mut self, info: I)
+    pub fn slice_collapse<I>(&mut self, info: &I)
     where
-        I: SliceArg<D>,
+        I: SliceArg<D> + ?Sized,
     {
         assert_eq!(
             info.in_ndim(),
@@ -479,16 +480,16 @@ where
         );
         let mut axis = 0;
         info.as_ref().iter().for_each(|&ax_info| match ax_info {
-                SliceInfoElem::Slice { start, end, step } => {
+                AxisSliceInfo::Slice { start, end, step } => {
                     self.slice_axis_inplace(Axis(axis), Slice { start, end, step });
                     axis += 1;
                 }
-                SliceInfoElem::Index(index) => {
+                AxisSliceInfo::Index(index) => {
                     let i_usize = abs_index(self.len_of(Axis(axis)), index);
                     self.collapse_axis(Axis(axis), i_usize);
                     axis += 1;
                 }
-                SliceInfoElem::NewAxis => panic!("`slice_collapse` does not support `NewAxis`."),
+                AxisSliceInfo::NewAxis => panic!("`slice_collapse` does not support `NewAxis`."),
             });
         debug_assert_eq!(axis, self.ndim());
     }
@@ -2049,6 +2050,23 @@ where
         self.zip_mut_with(rhs, |x, y| *x = y.clone());
     }
 
+    /// Perform an elementwise assigment of values cloned from `self` into array or producer `to`.
+    ///
+    /// The destination `to` can be another array or a producer of assignable elements.
+    /// [`AssignElem`] determines how elements are assigned.
+    ///
+    /// **Panics** if shapes disagree.
+    pub fn assign_to<P>(&self, to: P)
+    where
+        S: Data,
+        P: IntoNdProducer<Dim = D>,
+        P::Item: AssignElem<A>,
+        A: Clone,
+    {
+        Zip::from(self)
+            .map_assign_into(to, A::clone);
+    }
+
     /// Perform an elementwise assigment to `self` from element `x`.
     pub fn fill(&mut self, x: A)
     where
diff --git a/src/lib.rs b/src/lib.rs
index 263afa2d..778807af 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -87,8 +87,6 @@
 //!   - Enable transparent BLAS support for matrix multiplication.
 //!     Uses ``blas-src`` for pluggable backend, which needs to be configured
 //!     separately (see the README).
-//! - `matrixmultiply-threading`
-//!   - Enable the ``threading`` feature in the matrixmultiply package
 //!
 //! ## Documentation
 //!
@@ -144,7 +142,7 @@ pub use crate::dimension::NdIndex;
 pub use crate::error::{ErrorKind, ShapeError};
 pub use crate::indexes::{indices, indices_of};
 pub use crate::slice::{
-    MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceInfoElem, SliceNextDim,
+    AxisSliceInfo, MultiSliceArg, NewAxis, Slice, SliceArg, SliceInfo, SliceNextDim,
 };
 
 use crate::iterators::Baseiter;
@@ -208,7 +206,6 @@ mod shape_builder;
 mod slice;
 mod split_at;
 mod stacking;
-mod traversal_utils;
 #[macro_use]
 mod zip;
 
@@ -494,8 +491,9 @@ pub type Ixs = isize;
 ///
 /// The slicing argument can be passed using the macro [`s![]`](macro.s!.html),
 /// which will be used in all examples. (The explicit form is an instance of
-/// [`SliceInfo`] or another type which implements [`SliceArg`]; see their docs
-/// for more information.)
+/// [`&SliceInfo`]; see its docs for more information.)
+///
+/// [`&SliceInfo`]: struct.SliceInfo.html
 ///
 /// If a range is used, the axis is preserved. If an index is used, that index
 /// is selected and the axis is removed; this selects a subview. See
@@ -511,7 +509,7 @@ pub type Ixs = isize;
 /// [`NewAxis`]: struct.NewAxis.html
 ///
 /// When slicing arrays with generic dimensionality, creating an instance of
-/// [`SliceInfo`] to pass to the multi-axis slicing methods like [`.slice()`]
+/// [`&SliceInfo`] to pass to the multi-axis slicing methods like [`.slice()`]
 /// is awkward. In these cases, it's usually more convenient to use
 /// [`.slice_each_axis()`]/[`.slice_each_axis_mut()`]/[`.slice_each_axis_inplace()`]
 /// or to create a view and then slice individual axes of the view using
diff --git a/src/slice.rs b/src/slice.rs
index 3c554a5c..43785555 100644
--- a/src/slice.rs
+++ b/src/slice.rs
@@ -78,33 +78,33 @@ pub struct NewAxis;
 /// A slice (range with step), an index, or a new axis token.
 ///
 /// See also the [`s![]`](macro.s!.html) macro for a convenient way to create a
-/// `SliceInfo<[SliceInfoElem; n], Din, Dout>`.
+/// `&SliceInfo<[AxisSliceInfo; n], Din, Dout>`.
 ///
 /// ## Examples
 ///
-/// `SliceInfoElem::Index(a)` is the index `a`. It can also be created with
-/// `SliceInfoElem::from(a)`. The Python equivalent is `[a]`. The macro
+/// `AxisSliceInfo::Index(a)` is the index `a`. It can also be created with
+/// `AxisSliceInfo::from(a)`. The Python equivalent is `[a]`. The macro
 /// equivalent is `s![a]`.
 ///
-/// `SliceInfoElem::Slice { start: 0, end: None, step: 1 }` is the full range
-/// of an axis. It can also be created with `SliceInfoElem::from(..)`. The
+/// `AxisSliceInfo::Slice { start: 0, end: None, step: 1 }` is the full range
+/// of an axis. It can also be created with `AxisSliceInfo::from(..)`. The
 /// Python equivalent is `[:]`. The macro equivalent is `s![..]`.
 ///
-/// `SliceInfoElem::Slice { start: a, end: Some(b), step: 2 }` is every second
+/// `AxisSliceInfo::Slice { start: a, end: Some(b), step: 2 }` is every second
 /// element from `a` until `b`. It can also be created with
-/// `SliceInfoElem::from(Slice::from(a..b).step_by(2))`. The Python equivalent
-/// is `[a:b:2]`. The macro equivalent is `s![a..b;2]`.
+/// `AxisSliceInfo::from(a..b).step_by(2)`. The Python equivalent is `[a:b:2]`.
+/// The macro equivalent is `s![a..b;2]`.
 ///
-/// `SliceInfoElem::Slice { start: a, end: None, step: -1 }` is every element,
+/// `AxisSliceInfo::Slice { start: a, end: None, step: -1 }` is every element,
 /// from `a` until the end, in reverse order. It can also be created with
-/// `SliceInfoElem::from(Slice::from(a..).step_by(-1))`. The Python equivalent
-/// is `[a::-1]`. The macro equivalent is `s![a..;-1]`.
+/// `AxisSliceInfo::from(a..).step_by(-1)`. The Python equivalent is `[a::-1]`.
+/// The macro equivalent is `s![a..;-1]`.
 ///
-/// `SliceInfoElem::NewAxis` is a new axis of length 1. It can also be created
-/// with `SliceInfoElem::from(NewAxis)`. The Python equivalent is
+/// `AxisSliceInfo::NewAxis` is a new axis of length 1. It can also be created
+/// with `AxisSliceInfo::from(NewAxis)`. The Python equivalent is
 /// `[np.newaxis]`. The macro equivalent is `s![NewAxis]`.
 #[derive(Debug, PartialEq, Eq, Hash)]
-pub enum SliceInfoElem {
+pub enum AxisSliceInfo {
     /// A range with step size. `end` is an exclusive index. Negative `begin`
     /// or `end` indexes are counted from the back of the axis. If `end` is
     /// `None`, the slice extends to the end of the axis.
@@ -119,30 +119,54 @@ pub enum SliceInfoElem {
     NewAxis,
 }
 
-copy_and_clone! {SliceInfoElem}
+copy_and_clone! {AxisSliceInfo}
 
-impl SliceInfoElem {
+impl AxisSliceInfo {
     /// Returns `true` if `self` is a `Slice` value.
     pub fn is_slice(&self) -> bool {
-        matches!(self, SliceInfoElem::Slice { .. })
+        matches!(self, AxisSliceInfo::Slice { .. })
     }
 
     /// Returns `true` if `self` is an `Index` value.
     pub fn is_index(&self) -> bool {
-        matches!(self, SliceInfoElem::Index(_))
+        matches!(self, AxisSliceInfo::Index(_))
     }
 
     /// Returns `true` if `self` is a `NewAxis` value.
     pub fn is_new_axis(&self) -> bool {
-        matches!(self, SliceInfoElem::NewAxis)
+        matches!(self, AxisSliceInfo::NewAxis)
+    }
+
+    /// Returns a new `AxisSliceInfo` with the given step size (multiplied with
+    /// the previous step size).
+    ///
+    /// `step` must be nonzero.
+    /// (This method checks with a debug assertion that `step` is not zero.)
+    ///
+    /// **Panics** if `self` is not the `AxisSliceInfo::Slice` variant.
+    #[inline]
+    pub fn step_by(self, step: isize) -> Self {
+        debug_assert_ne!(step, 0, "AxisSliceInfo::step_by: step must be nonzero");
+        match self {
+            AxisSliceInfo::Slice {
+                start,
+                end,
+                step: orig_step,
+            } => AxisSliceInfo::Slice {
+                start,
+                end,
+                step: orig_step * step,
+            },
+            _ => panic!("AxisSliceInfo::step_by: `self` must be the `Slice` variant"),
+        }
     }
 }
 
-impl fmt::Display for SliceInfoElem {
+impl fmt::Display for AxisSliceInfo {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match *self {
-            SliceInfoElem::Index(index) => write!(f, "{}", index)?,
-            SliceInfoElem::Slice { start, end, step } => {
+            AxisSliceInfo::Index(index) => write!(f, "{}", index)?,
+            AxisSliceInfo::Slice { start, end, step } => {
                 if start != 0 {
                     write!(f, "{}", start)?;
                 }
@@ -154,7 +178,7 @@ impl fmt::Display for SliceInfoElem {
                     write!(f, ";{}", step)?;
                 }
             }
-            SliceInfoElem::NewAxis => write!(f, stringify!(NewAxis))?,
+            AxisSliceInfo::NewAxis => write!(f, stringify!(NewAxis))?,
         }
         Ok(())
     }
@@ -223,9 +247,9 @@ macro_rules! impl_slice_variant_from_range {
 impl_slice_variant_from_range!(Slice, Slice, isize);
 impl_slice_variant_from_range!(Slice, Slice, usize);
 impl_slice_variant_from_range!(Slice, Slice, i32);
-impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, isize);
-impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, usize);
-impl_slice_variant_from_range!(SliceInfoElem, SliceInfoElem::Slice, i32);
+impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, isize);
+impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, usize);
+impl_slice_variant_from_range!(AxisSliceInfo, AxisSliceInfo::Slice, i32);
 
 impl From<RangeFull> for Slice {
     #[inline]
@@ -238,10 +262,10 @@ impl From<RangeFull> for Slice {
     }
 }
 
-impl From<RangeFull> for SliceInfoElem {
+impl From<RangeFull> for AxisSliceInfo {
     #[inline]
-    fn from(_: RangeFull) -> SliceInfoElem {
-        SliceInfoElem::Slice {
+    fn from(_: RangeFull) -> AxisSliceInfo {
+        AxisSliceInfo::Slice {
             start: 0,
             end: None,
             step: 1,
@@ -249,10 +273,10 @@ impl From<RangeFull> for SliceInfoElem {
     }
 }
 
-impl From<Slice> for SliceInfoElem {
+impl From<Slice> for AxisSliceInfo {
     #[inline]
-    fn from(s: Slice) -> SliceInfoElem {
-        SliceInfoElem::Slice {
+    fn from(s: Slice) -> AxisSliceInfo {
+        AxisSliceInfo::Slice {
             start: s.start,
             end: s.end,
             step: s.step,
@@ -260,24 +284,24 @@ impl From<Slice> for SliceInfoElem {
     }
 }
 
-macro_rules! impl_sliceinfoelem_from_index {
+macro_rules! impl_axissliceinfo_from_index {
     ($index:ty) => {
-        impl From<$index> for SliceInfoElem {
+        impl From<$index> for AxisSliceInfo {
             #[inline]
-            fn from(r: $index) -> SliceInfoElem {
-                SliceInfoElem::Index(r as isize)
+            fn from(r: $index) -> AxisSliceInfo {
+                AxisSliceInfo::Index(r as isize)
             }
         }
     };
 }
-impl_sliceinfoelem_from_index!(isize);
-impl_sliceinfoelem_from_index!(usize);
-impl_sliceinfoelem_from_index!(i32);
+impl_axissliceinfo_from_index!(isize);
+impl_axissliceinfo_from_index!(usize);
+impl_axissliceinfo_from_index!(i32);
 
-impl From<NewAxis> for SliceInfoElem {
+impl From<NewAxis> for AxisSliceInfo {
     #[inline]
-    fn from(_: NewAxis) -> SliceInfoElem {
-        SliceInfoElem::NewAxis
+    fn from(_: NewAxis) -> AxisSliceInfo {
+        AxisSliceInfo::NewAxis
     }
 }
 
@@ -285,9 +309,9 @@ impl From<NewAxis> for SliceInfoElem {
 ///
 /// This trait is unsafe to implement because the implementation must ensure
 /// that `D`, `Self::OutDim`, `self.in_dim()`, and `self.out_ndim()` are
-/// consistent with the `&[SliceInfoElem]` returned by `self.as_ref()` and that
+/// consistent with the `&[AxisSliceInfo]` returned by `self.as_ref()` and that
 /// `self.as_ref()` always returns the same value when called multiple times.
-pub unsafe trait SliceArg<D: Dimension>: AsRef<[SliceInfoElem]> {
+pub unsafe trait SliceArg<D: Dimension>: AsRef<[AxisSliceInfo]> {
     /// Dimensionality of the output array.
     type OutDim: Dimension;
 
@@ -300,29 +324,11 @@ pub unsafe trait SliceArg<D: Dimension>: AsRef<[SliceInfoElem]> {
     private_decl! {}
 }
 
-unsafe impl<T, D> SliceArg<D> for &T
-where
-    T: SliceArg<D> + ?Sized,
-    D: Dimension,
-{
-    type OutDim = T::OutDim;
-
-    fn in_ndim(&self) -> usize {
-        T::in_ndim(self)
-    }
-
-    fn out_ndim(&self) -> usize {
-        T::out_ndim(self)
-    }
-
-    private_impl! {}
-}
-
 macro_rules! impl_slicearg_samedim {
     ($in_dim:ty) => {
         unsafe impl<T, Dout> SliceArg<$in_dim> for SliceInfo<T, $in_dim, Dout>
         where
-            T: AsRef<[SliceInfoElem]>,
+            T: AsRef<[AxisSliceInfo]>,
             Dout: Dimension,
         {
             type OutDim = Dout;
@@ -349,7 +355,7 @@ impl_slicearg_samedim!(Ix6);
 
 unsafe impl<T, Din, Dout> SliceArg<IxDyn> for SliceInfo<T, Din, Dout>
 where
-    T: AsRef<[SliceInfoElem]>,
+    T: AsRef<[AxisSliceInfo]>,
     Din: Dimension,
     Dout: Dimension,
 {
@@ -366,7 +372,7 @@ where
     private_impl! {}
 }
 
-unsafe impl SliceArg<IxDyn> for [SliceInfoElem] {
+unsafe impl SliceArg<IxDyn> for [AxisSliceInfo] {
     type OutDim = IxDyn;
 
     fn in_ndim(&self) -> usize {
@@ -382,8 +388,8 @@ unsafe impl SliceArg<IxDyn> for [SliceInfoElem] {
 
 /// Represents all of the necessary information to perform a slice.
 ///
-/// The type `T` is typically `[SliceInfoElem; n]`, `&[SliceInfoElem]`, or
-/// `Vec<SliceInfoElem>`. The type `Din` is the dimension of the array to be
+/// The type `T` is typically `[AxisSliceInfo; n]`, `[AxisSliceInfo]`, or
+/// `Vec<AxisSliceInfo>`. The type `Din` is the dimension of the array to be
 /// sliced, and `Dout` is the output dimension after calling [`.slice()`]. Note
 /// that if `Din` is a fixed dimension type (`Ix0`, `Ix1`, `Ix2`, etc.), the
 /// `SliceInfo` instance can still be used to slice an array with dimension
@@ -391,13 +397,14 @@ unsafe impl SliceArg<IxDyn> for [SliceInfoElem] {
 ///
 /// [`.slice()`]: struct.ArrayBase.html#method.slice
 #[derive(Debug)]
-pub struct SliceInfo<T, Din: Dimension, Dout: Dimension> {
+#[repr(transparent)]
+pub struct SliceInfo<T: ?Sized, Din: Dimension, Dout: Dimension> {
     in_dim: PhantomData<Din>,
     out_dim: PhantomData<Dout>,
     indices: T,
 }
 
-impl<T, Din, Dout> Deref for SliceInfo<T, Din, Dout>
+impl<T: ?Sized, Din, Dout> Deref for SliceInfo<T, Din, Dout>
 where
     Din: Dimension,
     Dout: Dimension,
@@ -408,7 +415,7 @@ where
     }
 }
 
-fn check_dims_for_sliceinfo<Din, Dout>(indices: &[SliceInfoElem]) -> Result<(), ShapeError>
+fn check_dims_for_sliceinfo<Din, Dout>(indices: &[AxisSliceInfo]) -> Result<(), ShapeError>
 where
     Din: Dimension,
     Dout: Dimension,
@@ -428,7 +435,7 @@ where
 
 impl<T, Din, Dout> SliceInfo<T, Din, Dout>
 where
-    T: AsRef<[SliceInfoElem]>,
+    T: AsRef<[AxisSliceInfo]>,
     Din: Dimension,
     Dout: Dimension,
 {
@@ -457,7 +464,14 @@ where
             indices,
         }
     }
+}
 
+impl<T, Din, Dout> SliceInfo<T, Din, Dout>
+where
+    T: AsRef<[AxisSliceInfo]>,
+    Din: Dimension,
+    Dout: Dimension,
+{
     /// Returns a new `SliceInfo` instance.
     ///
     /// Errors if `Din` or `Dout` is not consistent with `indices`.
@@ -476,13 +490,20 @@ where
             indices,
         })
     }
+}
 
+impl<T: ?Sized, Din, Dout> SliceInfo<T, Din, Dout>
+where
+    T: AsRef<[AxisSliceInfo]>,
+    Din: Dimension,
+    Dout: Dimension,
+{
     /// Returns the number of dimensions of the input array for
     /// [`.slice()`](struct.ArrayBase.html#method.slice).
     ///
     /// If `Din` is a fixed-size dimension type, then this is equivalent to
     /// `Din::NDIM.unwrap()`. Otherwise, the value is calculated by iterating
-    /// over the `SliceInfoElem` elements.
+    /// over the `AxisSliceInfo` elements.
     pub fn in_ndim(&self) -> usize {
         if let Some(ndim) = Din::NDIM {
             ndim
@@ -497,7 +518,7 @@ where
     ///
     /// If `Dout` is a fixed-size dimension type, then this is equivalent to
     /// `Dout::NDIM.unwrap()`. Otherwise, the value is calculated by iterating
-    /// over the `SliceInfoElem` elements.
+    /// over the `AxisSliceInfo` elements.
     pub fn out_ndim(&self) -> usize {
         if let Some(ndim) = Dout::NDIM {
             ndim
@@ -507,7 +528,7 @@ where
     }
 }
 
-impl<'a, Din, Dout> TryFrom<&'a [SliceInfoElem]> for SliceInfo<&'a [SliceInfoElem], Din, Dout>
+impl<'a, Din, Dout> TryFrom<&'a [AxisSliceInfo]> for &'a SliceInfo<[AxisSliceInfo], Din, Dout>
 where
     Din: Dimension,
     Dout: Dimension,
@@ -515,17 +536,22 @@ where
     type Error = ShapeError;
 
     fn try_from(
-        indices: &'a [SliceInfoElem],
-    ) -> Result<SliceInfo<&'a [SliceInfoElem], Din, Dout>, ShapeError> {
+        indices: &'a [AxisSliceInfo],
+    ) -> Result<&'a SliceInfo<[AxisSliceInfo], Din, Dout>, ShapeError> {
+        check_dims_for_sliceinfo::<Din, Dout>(indices)?;
         unsafe {
-            // This is okay because `&[SliceInfoElem]` always returns the same
-            // value for `.as_ref()`.
-            Self::new(indices)
+            // This is okay because we've already checked the correctness of
+            // `Din` and `Dout`, and the only non-zero-sized member of
+            // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], Din,
+            // Dout>` should have the same bitwise representation as
+            // `&[AxisSliceInfo]`.
+            Ok(&*(indices as *const [AxisSliceInfo]
+                as *const SliceInfo<[AxisSliceInfo], Din, Dout>))
         }
     }
 }
 
-impl<Din, Dout> TryFrom<Vec<SliceInfoElem>> for SliceInfo<Vec<SliceInfoElem>, Din, Dout>
+impl<Din, Dout> TryFrom<Vec<AxisSliceInfo>> for SliceInfo<Vec<AxisSliceInfo>, Din, Dout>
 where
     Din: Dimension,
     Dout: Dimension,
@@ -533,8 +559,8 @@ where
     type Error = ShapeError;
 
     fn try_from(
-        indices: Vec<SliceInfoElem>,
-    ) -> Result<SliceInfo<Vec<SliceInfoElem>, Din, Dout>, ShapeError> {
+        indices: Vec<AxisSliceInfo>,
+    ) -> Result<SliceInfo<Vec<AxisSliceInfo>, Din, Dout>, ShapeError> {
         unsafe {
             // This is okay because `Vec` always returns the same value for
             // `.as_ref()`.
@@ -545,8 +571,8 @@ where
 
 macro_rules! impl_tryfrom_array_for_sliceinfo {
     ($len:expr) => {
-        impl<Din, Dout> TryFrom<[SliceInfoElem; $len]>
-            for SliceInfo<[SliceInfoElem; $len], Din, Dout>
+        impl<Din, Dout> TryFrom<[AxisSliceInfo; $len]>
+            for SliceInfo<[AxisSliceInfo; $len], Din, Dout>
         where
             Din: Dimension,
             Dout: Dimension,
@@ -554,10 +580,10 @@ macro_rules! impl_tryfrom_array_for_sliceinfo {
             type Error = ShapeError;
 
             fn try_from(
-                indices: [SliceInfoElem; $len],
-            ) -> Result<SliceInfo<[SliceInfoElem; $len], Din, Dout>, ShapeError> {
+                indices: [AxisSliceInfo; $len],
+            ) -> Result<SliceInfo<[AxisSliceInfo; $len], Din, Dout>, ShapeError> {
                 unsafe {
-                    // This is okay because `[SliceInfoElem; N]` always returns
+                    // This is okay because `[AxisSliceInfo; N]` always returns
                     // the same value for `.as_ref()`.
                     Self::new(indices)
                 }
@@ -575,29 +601,31 @@ impl_tryfrom_array_for_sliceinfo!(6);
 impl_tryfrom_array_for_sliceinfo!(7);
 impl_tryfrom_array_for_sliceinfo!(8);
 
-impl<T, Din, Dout> AsRef<[SliceInfoElem]> for SliceInfo<T, Din, Dout>
+impl<T, Din, Dout> AsRef<[AxisSliceInfo]> for SliceInfo<T, Din, Dout>
 where
-    T: AsRef<[SliceInfoElem]>,
+    T: AsRef<[AxisSliceInfo]>,
     Din: Dimension,
     Dout: Dimension,
 {
-    fn as_ref(&self) -> &[SliceInfoElem] {
+    fn as_ref(&self) -> &[AxisSliceInfo] {
         self.indices.as_ref()
     }
 }
 
-impl<'a, T, Din, Dout> From<&'a SliceInfo<T, Din, Dout>>
-    for SliceInfo<&'a [SliceInfoElem], Din, Dout>
+impl<T, Din, Dout> AsRef<SliceInfo<[AxisSliceInfo], Din, Dout>> for SliceInfo<T, Din, Dout>
 where
-    T: AsRef<[SliceInfoElem]>,
+    T: AsRef<[AxisSliceInfo]>,
     Din: Dimension,
     Dout: Dimension,
 {
-    fn from(info: &'a SliceInfo<T, Din, Dout>) -> SliceInfo<&'a [SliceInfoElem], Din, Dout> {
-        SliceInfo {
-            in_dim: info.in_dim,
-            out_dim: info.out_dim,
-            indices: info.indices.as_ref(),
+    fn as_ref(&self) -> &SliceInfo<[AxisSliceInfo], Din, Dout> {
+        unsafe {
+            // This is okay because the only non-zero-sized member of
+            // `SliceInfo` is `indices`, so `&SliceInfo<[AxisSliceInfo], Din, Dout>`
+            // should have the same bitwise representation as
+            // `&[AxisSliceInfo]`.
+            &*(self.indices.as_ref() as *const [AxisSliceInfo]
+                as *const SliceInfo<[AxisSliceInfo], Din, Dout>)
         }
     }
 }
@@ -675,14 +703,16 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1);
 ///
 /// `s![]` takes a list of ranges/slices/indices/new-axes, separated by comma,
 /// with optional step sizes that are separated from the range by a semicolon.
-/// It is converted into a [`SliceInfo`] instance.
+/// It is converted into a [`&SliceInfo`] instance.
+///
+/// [`&SliceInfo`]: struct.SliceInfo.html
 ///
 /// Each range/slice/index uses signed indices, where a negative value is
 /// counted from the end of the axis. Step sizes are also signed and may be
 /// negative, but must not be zero.
 ///
-/// The syntax is `s![` *[ elem [, elem [ , ... ] ] ]* `]`, where *elem* is any
-/// of the following:
+/// The syntax is `s![` *[ axis-slice-info [, axis-slice-info [ , ... ] ] ]*
+/// `]`, where *axis-slice-info* is any of the following:
 ///
 /// * *index*: an index to use for taking a subview with respect to that axis.
 ///   (The index is selected. The axis is removed except with
@@ -690,15 +720,15 @@ impl_slicenextdim!((), NewAxis, Ix0, Ix1);
 /// * *range*: a range with step size 1 to use for slicing that axis.
 /// * *range* `;` *step*: a range with step size *step* to use for slicing that axis.
 /// * *slice*: a [`Slice`] instance to use for slicing that axis.
-/// * *slice* `;` *step*: a range constructed from a [`Slice`] instance,
-///   multiplying the step size by *step*, to use for slicing that axis.
+/// * *slice* `;` *step*: a range constructed from the start and end of a [`Slice`]
+///   instance, with new step size *step*, to use for slicing that axis.
 /// * *new-axis*: a [`NewAxis`] instance that represents the creation of a new axis.
 ///   (Except for [`.slice_collapse()`], which panics on [`NewAxis`] elements.)
 ///
 /// [`Slice`]: struct.Slice.html
 /// [`NewAxis`]: struct.NewAxis.html
 ///
-/// The number of *elem*, not including *new-axis*, must match the
+/// The number of *axis-slice-info*, not including *new-axis*, must match the
 /// number of axes in the array. *index*, *range*, *slice*, *step*, and
 /// *new-axis* can be expressions. *index* must be of type `isize`, `usize`, or
 /// `i32`. *range* must be of type `Range<I>`, `RangeTo<I>`, `RangeFrom<I>`, or
@@ -850,18 +880,18 @@ macro_rules! s(
     };
     // Catch-all clause for syntax errors
     (@parse $($t:tt)*) => { compile_error!("Invalid syntax in s![] call.") };
-    // convert range/index/new-axis into SliceInfoElem
+    // convert range/index/new-axis into AxisSliceInfo
     (@convert $r:expr) => {
-        <$crate::SliceInfoElem as ::std::convert::From<_>>::from($r)
+        <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r)
     };
-    // convert range/index/new-axis and step into SliceInfoElem
+    // convert range/index/new-axis and step into AxisSliceInfo
     (@convert $r:expr, $s:expr) => {
-        <$crate::SliceInfoElem as ::std::convert::From<_>>::from(
-            <$crate::Slice as ::std::convert::From<_>>::from($r).step_by($s as isize)
-        )
+        <$crate::AxisSliceInfo as ::std::convert::From<_>>::from($r).step_by($s as isize)
     };
     ($($t:tt)*) => {
-        $crate::s![@parse
+        // The extra `*&` is a workaround for this compiler bug:
+        // https://github.com/rust-lang/rust/issues/23014
+        &*&$crate::s![@parse
               ::std::marker::PhantomData::<$crate::Ix0>,
               ::std::marker::PhantomData::<$crate::Ix0>,
               []
@@ -903,7 +933,7 @@ where
     private_impl! {}
 }
 
-impl<'a, A, D, I0> MultiSliceArg<'a, A, D> for (I0,)
+impl<'a, A, D, I0> MultiSliceArg<'a, A, D> for (&I0,)
 where
     A: 'a,
     D: Dimension,
@@ -912,7 +942,7 @@ where
     type Output = (ArrayViewMut<'a, A, I0::OutDim>,);
 
     fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output {
-        (view.slice_move(&self.0),)
+        (view.slice_move(self.0),)
     }
 
     private_impl! {}
@@ -923,7 +953,7 @@ macro_rules! impl_multislice_tuple {
         impl_multislice_tuple!(@def_impl ($($but_last,)* $last,), [$($but_last)*] $last);
     };
     (@def_impl ($($all:ident,)*), [$($but_last:ident)*] $last:ident) => {
-        impl<'a, A, D, $($all,)*> MultiSliceArg<'a, A, D> for ($($all,)*)
+        impl<'a, A, D, $($all,)*> MultiSliceArg<'a, A, D> for ($(&$all,)*)
         where
             A: 'a,
             D: Dimension,
@@ -933,7 +963,7 @@ macro_rules! impl_multislice_tuple {
 
             fn multi_slice_move(&self, view: ArrayViewMut<'a, A, D>) -> Self::Output {
                 #[allow(non_snake_case)]
-                let ($($all,)*) = self;
+                let &($($all,)*) = self;
 
                 let shape = view.raw_dim();
                 assert!(!impl_multislice_tuple!(@intersects_self &shape, ($($all,)*)));
diff --git a/src/stacking.rs b/src/stacking.rs
index 580d94b4..500ded6a 100644
--- a/src/stacking.rs
+++ b/src/stacking.rs
@@ -8,7 +8,6 @@
 
 use crate::error::{from_kind, ErrorKind, ShapeError};
 use crate::imp_prelude::*;
-use crate::traversal_utils::assign_to;
 
 /// Stack arrays along the new axis.
 ///
@@ -99,7 +98,7 @@ where
         for array in arrays {
             let len = array.len_of(axis);
             let (front, rest) = assign_view.split_at(axis, len);
-            assign_to(array, front);
+            array.assign_to(front);
             assign_view = rest;
         }
         debug_assert_eq!(assign_view.len(), 0);
@@ -171,7 +170,7 @@ where
             // but same number of axes).
             let assign_view = assign_view.into_dimensionality::<D>()
                 .expect("same-dimensionality cast");
-            assign_to(array, assign_view);
+            array.assign_to(assign_view);
         });
 
     unsafe {
diff --git a/src/traversal_utils.rs b/src/traversal_utils.rs
deleted file mode 100644
index 3f4d017b..00000000
--- a/src/traversal_utils.rs
+++ /dev/null
@@ -1,26 +0,0 @@
-// Copyright 2020 bluss and ndarray developers.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use crate::{
-    IntoNdProducer,
-    AssignElem,
-    Zip,
-};
-
-/// Assign values from producer P1 to producer P2
-/// P1 and P2 must be of the same shape and dimension
-pub(crate) fn assign_to<'a, P1, P2, A>(from: P1, to: P2)
-    where P1: IntoNdProducer<Item = &'a A>,
-          P2: IntoNdProducer<Dim = P1::Dim>,
-          P2::Item: AssignElem<A>,
-          A: Clone + 'a
-{
-    Zip::from(from)
-        .map_assign_into(to, A::clone);
-}
-


Test patch:
diff --git a/blas-tests/tests/oper.rs b/blas-tests/tests/oper.rs
index d6230cce..6b6797f1 100644
--- a/blas-tests/tests/oper.rs
+++ b/blas-tests/tests/oper.rs
@@ -6,7 +6,8 @@ extern crate num_traits;
 use ndarray::linalg::general_mat_mul;
 use ndarray::linalg::general_mat_vec_mul;
 use ndarray::prelude::*;
-use ndarray::{Data, Ix, Ixs, LinalgScalar, Slice, SliceInfoElem};
+use ndarray::{AxisSliceInfo, Ix, Ixs};
+use ndarray::{Data, LinalgScalar};
 
 use approx::{assert_abs_diff_eq, assert_relative_eq};
 use defmac::defmac;
@@ -418,12 +419,12 @@ fn scaled_add_3() {
                 let mut a = range_mat64(m, k);
                 let mut answer = a.clone();
                 let cdim = if n == 1 { vec![q] } else { vec![n, q] };
-                let cslice: Vec<SliceInfoElem> = if n == 1 {
-                    vec![Slice::from(..).step_by(s2).into()]
+                let cslice = if n == 1 {
+                    vec![AxisSliceInfo::from(..).step_by(s2)]
                 } else {
                     vec![
-                        Slice::from(..).step_by(s1).into(),
-                        Slice::from(..).step_by(s2).into(),
+                        AxisSliceInfo::from(..).step_by(s1),
+                        AxisSliceInfo::from(..).step_by(s2),
                     ]
                 };
 
diff --git a/tests/array.rs b/tests/array.rs
index 458b8e79..28e305f5 100644
--- a/tests/array.rs
+++ b/tests/array.rs
@@ -12,7 +12,7 @@ use itertools::{enumerate, zip, Itertools};
 use ndarray::prelude::*;
 use ndarray::{arr3, rcarr2};
 use ndarray::indices;
-use ndarray::{Slice, SliceInfo, SliceInfoElem};
+use ndarray::{AxisSliceInfo, Slice, SliceInfo};
 use std::convert::TryFrom;
 
 macro_rules! assert_panics {
@@ -219,20 +219,20 @@ fn test_slice_dyninput_array_fixed() {
 #[test]
 fn test_slice_array_dyn() {
     let mut arr = Array3::<f64>::zeros((5, 2, 5));
-    let info = SliceInfo::<_, Ix3, IxDyn>::try_from([
-        SliceInfoElem::from(1..),
-        SliceInfoElem::from(1),
-        SliceInfoElem::from(NewAxis),
-        SliceInfoElem::from(Slice::from(..).step_by(2)),
+    let info = &SliceInfo::<_, Ix3, IxDyn>::try_from([
+        AxisSliceInfo::from(1..),
+        AxisSliceInfo::from(1),
+        AxisSliceInfo::from(NewAxis),
+        AxisSliceInfo::from(..).step_by(2),
     ])
     .unwrap();
     arr.slice(info);
     arr.slice_mut(info);
     arr.view().slice_move(info);
-    let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([
-        SliceInfoElem::from(1..),
-        SliceInfoElem::from(1),
-        SliceInfoElem::from(Slice::from(..).step_by(2)),
+    let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from([
+        AxisSliceInfo::from(1..),
+        AxisSliceInfo::from(1),
+        AxisSliceInfo::from(..).step_by(2),
     ])
     .unwrap();
     arr.view().slice_collapse(info2);
@@ -241,20 +241,20 @@ fn test_slice_array_dyn() {
 #[test]
 fn test_slice_dyninput_array_dyn() {
     let mut arr = Array3::<f64>::zeros((5, 2, 5)).into_dyn();
-    let info = SliceInfo::<_, Ix3, IxDyn>::try_from([
-        SliceInfoElem::from(1..),
-        SliceInfoElem::from(1),
-        SliceInfoElem::from(NewAxis),
-        SliceInfoElem::from(Slice::from(..).step_by(2)),
+    let info = &SliceInfo::<_, Ix3, IxDyn>::try_from([
+        AxisSliceInfo::from(1..),
+        AxisSliceInfo::from(1),
+        AxisSliceInfo::from(NewAxis),
+        AxisSliceInfo::from(..).step_by(2),
     ])
     .unwrap();
     arr.slice(info);
     arr.slice_mut(info);
     arr.view().slice_move(info);
-    let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from([
-        SliceInfoElem::from(1..),
-        SliceInfoElem::from(1),
-        SliceInfoElem::from(Slice::from(..).step_by(2)),
+    let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from([
+        AxisSliceInfo::from(1..),
+        AxisSliceInfo::from(1),
+        AxisSliceInfo::from(..).step_by(2),
     ])
     .unwrap();
     arr.view().slice_collapse(info2);
@@ -264,19 +264,19 @@ fn test_slice_dyninput_array_dyn() {
 fn test_slice_dyninput_vec_fixed() {
     let mut arr = Array3::<f64>::zeros((5, 2, 5)).into_dyn();
     let info = &SliceInfo::<_, Ix3, Ix3>::try_from(vec![
-        SliceInfoElem::from(1..),
-        SliceInfoElem::from(1),
-        SliceInfoElem::from(NewAxis),
-        SliceInfoElem::from(Slice::from(..).step_by(2)),
+        AxisSliceInfo::from(1..),
+        AxisSliceInfo::from(1),
+        AxisSliceInfo::from(NewAxis),
+        AxisSliceInfo::from(..).step_by(2),
     ])
     .unwrap();
     arr.slice(info);
     arr.slice_mut(info);
     arr.view().slice_move(info);
-    let info2 = SliceInfo::<_, Ix3, Ix2>::try_from(vec![
-        SliceInfoElem::from(1..),
-        SliceInfoElem::from(1),
-        SliceInfoElem::from(Slice::from(..).step_by(2)),
+    let info2 = &SliceInfo::<_, Ix3, Ix2>::try_from(vec![
+        AxisSliceInfo::from(1..),
+        AxisSliceInfo::from(1),
+        AxisSliceInfo::from(..).step_by(2),
     ])
     .unwrap();
     arr.view().slice_collapse(info2);
@@ -286,19 +286,19 @@ fn test_slice_dyninput_vec_fixed() {
 fn test_slice_dyninput_vec_dyn() {
     let mut arr = Array3::<f64>::zeros((5, 2, 5)).into_dyn();
     let info = &SliceInfo::<_, Ix3, IxDyn>::try_from(vec![
-        SliceInfoElem::from(1..),
-        SliceInfoElem::from(1),
-        SliceInfoElem::from(NewAxis),
-        SliceInfoElem::from(Slice::from(..).step_by(2)),
+        AxisSliceInfo::from(1..),
+        AxisSliceInfo::from(1),
+        AxisSliceInfo::from(NewAxis),
+        AxisSliceInfo::from(..).step_by(2),
     ])
     .unwrap();
     arr.slice(info);
     arr.slice_mut(info);
     arr.view().slice_move(info);
-    let info2 = SliceInfo::<_, Ix3, IxDyn>::try_from(vec![
-        SliceInfoElem::from(1..),
-        SliceInfoElem::from(1),
-        SliceInfoElem::from(Slice::from(..).step_by(2)),
+    let info2 = &SliceInfo::<_, Ix3, IxDyn>::try_from(vec![
+        AxisSliceInfo::from(1..),
+        AxisSliceInfo::from(1),
+        AxisSliceInfo::from(..).step_by(2),
     ])
     .unwrap();
     arr.view().slice_collapse(info2);
@@ -900,31 +900,6 @@ fn standard_layout() {
     assert!(x4.is_standard_layout());
 }
 
-#[test]
-fn assign() {
-    let mut a = arr2(&[[1., 2.], [3., 4.]]);
-    let b = arr2(&[[1., 3.], [2., 4.]]);
-    a.assign(&b);
-    assert_eq!(a, b);
-
-    /* Test broadcasting */
-    a.assign(&ArcArray::zeros(1));
-    assert_eq!(a, ArcArray::zeros((2, 2)));
-
-    /* Test other type */
-    a.assign(&Array::from_elem((2, 2), 3.));
-    assert_eq!(a, ArcArray::from_elem((2, 2), 3.));
-
-    /* Test mut view */
-    let mut a = arr2(&[[1, 2], [3, 4]]);
-    {
-        let mut v = a.view_mut();
-        v.slice_collapse(s![..1, ..]);
-        v.fill(0);
-    }
-    assert_eq!(a, arr2(&[[0, 0], [3, 4]]));
-}
-
 #[test]
 fn iter_size_hint() {
     let mut a = arr2(&[[1., 2.], [3., 4.]]);
diff --git a/tests/higher_order_f.rs b/tests/higher_order_f.rs
index c567eb3e..1238cc4d 100644
--- a/tests/higher_order_f.rs
+++ b/tests/higher_order_f.rs
@@ -6,3 +6,37 @@ fn test_fold_axis_oob() {
     let a = arr2(&[[1., 2.], [3., 4.]]);
     a.fold_axis(Axis(2), 0., |x, y| x + y);
 }
+
+#[test]
+fn assign() {
+    let mut a = arr2(&[[1., 2.], [3., 4.]]);
+    let b = arr2(&[[1., 3.], [2., 4.]]);
+    a.assign(&b);
+    assert_eq!(a, b);
+
+    /* Test broadcasting */
+    a.assign(&ArcArray::zeros(1));
+    assert_eq!(a, ArcArray::zeros((2, 2)));
+
+    /* Test other type */
+    a.assign(&Array::from_elem((2, 2), 3.));
+    assert_eq!(a, ArcArray::from_elem((2, 2), 3.));
+
+    /* Test mut view */
+    let mut a = arr2(&[[1, 2], [3, 4]]);
+    {
+        let mut v = a.view_mut();
+        v.slice_collapse(s![..1, ..]);
+        v.fill(0);
+    }
+    assert_eq!(a, arr2(&[[0, 0], [3, 4]]));
+}
+
+
+#[test]
+fn assign_to() {
+    let mut a = arr2(&[[1., 2.], [3., 4.]]);
+    let b = arr2(&[[0., 3.], [2., 0.]]);
+    b.assign_to(&mut a);
+    assert_eq!(a, b);
+}
diff --git a/tests/oper.rs b/tests/oper.rs
index ed612bad..b91a9bd8 100644
--- a/tests/oper.rs
+++ b/tests/oper.rs
@@ -561,7 +561,7 @@ fn scaled_add_2() {
 #[test]
 fn scaled_add_3() {
     use approx::assert_relative_eq;
-    use ndarray::{Slice, SliceInfo, SliceInfoElem};
+    use ndarray::{SliceInfo, AxisSliceInfo};
     use std::convert::TryFrom;
 
     let beta = -2.3;
@@ -583,12 +583,12 @@ fn scaled_add_3() {
                 let mut a = range_mat64(m, k);
                 let mut answer = a.clone();
                 let cdim = if n == 1 { vec![q] } else { vec![n, q] };
-                let cslice: Vec<SliceInfoElem> = if n == 1 {
-                    vec![Slice::from(..).step_by(s2).into()]
+                let cslice = if n == 1 {
+                    vec![AxisSliceInfo::from(..).step_by(s2)]
                 } else {
                     vec![
-                        Slice::from(..).step_by(s1).into(),
-                        Slice::from(..).step_by(s2).into(),
+                        AxisSliceInfo::from(..).step_by(s1),
+                        AxisSliceInfo::from(..).step_by(s2),
                     ]
                 };
 
@@ -596,7 +596,7 @@ fn scaled_add_3() {
 
                 {
                     let mut av = a.slice_mut(s![..;s1, ..;s2]);
-                    let c = c.slice(SliceInfo::<_, IxDyn, IxDyn>::try_from(cslice).unwrap());
+                    let c = c.slice(&SliceInfo::<_, IxDyn, IxDyn>::try_from(cslice).unwrap());
 
                     let mut answerv = answer.slice_mut(s![..;s1, ..;s2]);
                     answerv += &(beta * &c);