Repository: rust-ndarray/ndarray (rust)

Issue:
`ArrayBase::as_slice_memory_order` returns `None` when slice has a single element
For some reason, `as_slice_memory_order` returns `None` when called on a 1-element slice. Minimal example:

```rust
use ndarray::{s, Array1};
let arr = Array1::from_vec(vec![1.0, 2.0, 3.0]);
println!("{:?}", arr.slice(s![0..1]).as_slice_memory_order());
// Prints None
```

Pull request description:
Fix contig check for single element arrays
When an array has 0 or 1 elements, strides don't matter anymore. The general case of this function handled this correctly, but the special case for ndim == 1 did not.

Fixes #1359

Patch:
diff --git a/src/array_serde.rs b/src/array_serde.rs
index f1c9e121..a6f3c617 100644
--- a/src/array_serde.rs
+++ b/src/array_serde.rs
@@ -12,6 +12,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer};
 use std::fmt;
 use std::marker::PhantomData;
 use alloc::format;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 use crate::imp_prelude::*;
diff --git a/src/arrayformat.rs b/src/arrayformat.rs
index ae781df6..ec5b041d 100644
--- a/src/arrayformat.rs
+++ b/src/arrayformat.rs
@@ -285,8 +285,9 @@ where
 #[cfg(test)]
 mod formatting_with_omit {
     use itertools::Itertools;
-    use std::fmt;
+    #[cfg(not(feature = "std"))]
     use alloc::string::String;
+    #[cfg(not(feature = "std"))]
     use alloc::vec::Vec;
 
     use super::*;
diff --git a/src/arraytraits.rs b/src/arraytraits.rs
index 9aa45bde..f45c8a1b 100644
--- a/src/arraytraits.rs
+++ b/src/arraytraits.rs
@@ -6,9 +6,10 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[cfg(not(feature = "std"))]
 use alloc::boxed::Box;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
-use std::iter::IntoIterator;
 use std::mem;
 use std::ops::{Index, IndexMut};
 use std::{hash, mem::size_of};
@@ -115,6 +116,7 @@ where
 
 /// Return `true` if the array shapes and all elements of `self` and
 /// `rhs` are equal. Return `false` otherwise.
+#[allow(clippy::unconditional_recursion)] // false positive
 impl<'a, A, B, S, S2, D> PartialEq<&'a ArrayBase<S2, D>> for ArrayBase<S, D>
 where
     A: PartialEq<B>,
@@ -129,6 +131,7 @@ where
 
 /// Return `true` if the array shapes and all elements of `self` and
 /// `rhs` are equal. Return `false` otherwise.
+#[allow(clippy::unconditional_recursion)] // false positive
 impl<'a, A, B, S, S2, D> PartialEq<ArrayBase<S2, D>> for &'a ArrayBase<S, D>
 where
     A: PartialEq<B>,
diff --git a/src/data_repr.rs b/src/data_repr.rs
index 6630f9dd..f740988f 100644
--- a/src/data_repr.rs
+++ b/src/data_repr.rs
@@ -2,7 +2,9 @@ use std::mem;
 use std::mem::ManuallyDrop;
 use std::ptr::NonNull;
 use alloc::slice;
+#[cfg(not(feature = "std"))]
 use alloc::borrow::ToOwned;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 use crate::extension::nonnull;
 
diff --git a/src/data_traits.rs b/src/data_traits.rs
index acf4b0b7..50e5a3e6 100644
--- a/src/data_traits.rs
+++ b/src/data_traits.rs
@@ -14,6 +14,7 @@ use std::mem::{self, size_of};
 use std::mem::MaybeUninit;
 use std::ptr::NonNull;
 use alloc::sync::Arc;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 use crate::{
diff --git a/src/dimension/axes.rs b/src/dimension/axes.rs
index fb1ff099..5660675b 100644
--- a/src/dimension/axes.rs
+++ b/src/dimension/axes.rs
@@ -143,7 +143,6 @@ where
 
 trait IncOps: Copy {
     fn post_inc(&mut self) -> Self;
-    fn post_dec(&mut self) -> Self;
     fn pre_dec(&mut self) -> Self;
 }
 
@@ -155,12 +154,6 @@ impl IncOps for usize {
         x
     }
     #[inline(always)]
-    fn post_dec(&mut self) -> Self {
-        let x = *self;
-        *self -= 1;
-        x
-    }
-    #[inline(always)]
     fn pre_dec(&mut self) -> Self {
         *self -= 1;
         *self
diff --git a/src/dimension/conversion.rs b/src/dimension/conversion.rs
index 6b53a4ee..f6c408a7 100644
--- a/src/dimension/conversion.rs
+++ b/src/dimension/conversion.rs
@@ -10,6 +10,7 @@
 
 use num_traits::Zero;
 use std::ops::{Index, IndexMut};
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 use crate::{Dim, Dimension, Ix, Ix1, IxDyn, IxDynImpl};
diff --git a/src/dimension/dimension_trait.rs b/src/dimension/dimension_trait.rs
index 7a3955fb..c594859e 100644
--- a/src/dimension/dimension_trait.rs
+++ b/src/dimension/dimension_trait.rs
@@ -9,6 +9,7 @@
 use std::fmt::Debug;
 use std::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
 use std::ops::{Index, IndexMut};
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 use super::axes_of;
@@ -284,21 +285,25 @@ pub trait Dimension:
             return true;
         }
         if dim.ndim() == 1 {
-            return strides[0] as isize == -1;
-        }
-        let order = strides._fastest_varying_stride_order();
-        let strides = strides.slice();
-
-        let dim_slice = dim.slice();
-        let mut cstride = 1;
-        for &i in order.slice() {
-            // a dimension of length 1 can have unequal strides
-            if dim_slice[i] != 1 && (strides[i] as isize).unsigned_abs() != cstride {
-                return false;
+            // fast case for ndim == 1:
+            // Either we have length <= 1, then stride is arbitrary,
+            // or we have stride == 1 or stride == -1, but +1 case is already handled above.
+            dim[0] <= 1 || strides[0] as isize == -1
+        } else {
+            let order = strides._fastest_varying_stride_order();
+            let strides = strides.slice();
+
+            let dim_slice = dim.slice();
+            let mut cstride = 1;
+            for &i in order.slice() {
+                // a dimension of length 1 can have unequal strides
+                if dim_slice[i] != 1 && (strides[i] as isize).unsigned_abs() != cstride {
+                    return false;
+                }
+                cstride *= dim_slice[i];
             }
-            cstride *= dim_slice[i];
+            true
         }
-        true
     }
 
     /// Return the axis ordering corresponding to the fastest variation
diff --git a/src/dimension/dynindeximpl.rs b/src/dimension/dynindeximpl.rs
index e81bf4f3..c2aea032 100644
--- a/src/dimension/dynindeximpl.rs
+++ b/src/dimension/dynindeximpl.rs
@@ -2,7 +2,9 @@ use crate::imp_prelude::*;
 use std::hash::{Hash, Hasher};
 use std::ops::{Deref, DerefMut, Index, IndexMut};
 use alloc::vec;
+#[cfg(not(feature = "std"))]
 use alloc::boxed::Box;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 const CAP: usize = 4;
 
diff --git a/src/extension/nonnull.rs b/src/extension/nonnull.rs
index 5aa50fdc..4deee11a 100644
--- a/src/extension/nonnull.rs
+++ b/src/extension/nonnull.rs
@@ -1,4 +1,5 @@
 use std::ptr::NonNull;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 /// Return a NonNull<T> pointer to the vector's data
diff --git a/src/free_functions.rs b/src/free_functions.rs
index 156eee6b..e9c3abff 100644
--- a/src/free_functions.rs
+++ b/src/free_functions.rs
@@ -7,6 +7,7 @@
 // except according to those terms.
 
 use alloc::vec;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 use std::mem::{forget, size_of};
 
diff --git a/src/impl_1d.rs b/src/impl_1d.rs
index 17f7729d..a9fe8440 100644
--- a/src/impl_1d.rs
+++ b/src/impl_1d.rs
@@ -7,6 +7,7 @@
 // except according to those terms.
 
 //! Methods for one-dimensional arrays.
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 use std::mem::MaybeUninit;
 
diff --git a/src/impl_constructors.rs b/src/impl_constructors.rs
index 049384ce..2779e27d 100644
--- a/src/impl_constructors.rs
+++ b/src/impl_constructors.rs
@@ -17,6 +17,7 @@ use num_traits::{One, Zero};
 use std::mem;
 use std::mem::MaybeUninit;
 use alloc::vec;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 use crate::dimension;
diff --git a/src/impl_methods.rs b/src/impl_methods.rs
index 0b6d0b2e..19249f9e 100644
--- a/src/impl_methods.rs
+++ b/src/impl_methods.rs
@@ -9,6 +9,7 @@
 use std::mem::{size_of, ManuallyDrop};
 use alloc::slice;
 use alloc::vec;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 use rawpointer::PointerExt;
 
diff --git a/src/impl_ops.rs b/src/impl_ops.rs
index a267198b..9eacd6b6 100644
--- a/src/impl_ops.rs
+++ b/src/impl_ops.rs
@@ -288,7 +288,6 @@ mod arithmetic_ops {
     use super::*;
     use crate::imp_prelude::*;
 
-    use num_complex::Complex;
     use std::ops::*;
 
     fn clone_opf<A: Clone, B: Clone, C>(f: impl Fn(A, B) -> C) -> impl FnMut(&A, &B) -> C {
diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs
index 8cfb82b5..aaa9f30b 100644
--- a/src/impl_owned_array.rs
+++ b/src/impl_owned_array.rs
@@ -1,4 +1,5 @@
 
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 use std::mem;
 use std::mem::MaybeUninit;
diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs
index 063b2205..6bac471b 100644
--- a/src/iterators/mod.rs
+++ b/src/iterators/mod.rs
@@ -17,6 +17,7 @@ mod windows;
 use std::iter::FromIterator;
 use std::marker::PhantomData;
 use std::ptr;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 use crate::Ix1;
diff --git a/src/linalg/impl_linalg.rs b/src/linalg/impl_linalg.rs
index 52a15f44..6a4542ed 100644
--- a/src/linalg/impl_linalg.rs
+++ b/src/linalg/impl_linalg.rs
@@ -16,6 +16,7 @@ use crate::{LinalgScalar, Zip};
 
 use std::any::TypeId;
 use std::mem::MaybeUninit;
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 use num_complex::Complex;
diff --git a/src/numeric/impl_numeric.rs b/src/numeric/impl_numeric.rs
index afcd7e89..0a720cb3 100644
--- a/src/numeric/impl_numeric.rs
+++ b/src/numeric/impl_numeric.rs
@@ -8,7 +8,7 @@
 
 #[cfg(feature = "std")]
 use num_traits::Float;
-use num_traits::{self, FromPrimitive, Zero};
+use num_traits::{FromPrimitive, Zero};
 use std::ops::{Add, Div, Mul};
 
 use crate::imp_prelude::*;
diff --git a/src/slice.rs b/src/slice.rs
index 0146d6db..182d3723 100644
--- a/src/slice.rs
+++ b/src/slice.rs
@@ -8,6 +8,7 @@
 use crate::dimension::slices_intersect;
 use crate::error::{ErrorKind, ShapeError};
 use crate::{ArrayViewMut, DimAdd, Dimension, Ix0, Ix1, Ix2, Ix3, Ix4, Ix5, Ix6, IxDyn};
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 use std::convert::TryFrom;
 use std::fmt;
diff --git a/src/split_at.rs b/src/split_at.rs
index b05e5834..50466afd 100644
--- a/src/split_at.rs
+++ b/src/split_at.rs
@@ -7,6 +7,7 @@ pub(crate) trait SplitAt  {
 }
 
 pub(crate) trait SplitPreference : SplitAt {
+    #[allow(dead_code)] // used only when Rayon support is enabled
     fn can_split(&self) -> bool;
     fn split_preference(&self) -> (Axis, usize);
     fn split(self) -> (Self, Self) where Self: Sized {
diff --git a/src/stacking.rs b/src/stacking.rs
index 5a47589d..16058f39 100644
--- a/src/stacking.rs
+++ b/src/stacking.rs
@@ -6,6 +6,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+#[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
 
 use crate::dimension;


Test patch:
diff --git a/tests/array.rs b/tests/array.rs
index 8fdbb999..d770d782 100644
--- a/tests/array.rs
+++ b/tests/array.rs
@@ -1957,6 +1957,22 @@ fn test_contiguous() {
     assert!(b.as_slice_memory_order().is_some());
 }
 
+#[test]
+fn test_contiguous_single_element()
+{
+    assert_matches!(array![1].as_slice_memory_order(), Some(&[1]));
+
+    let arr1 = array![1, 2, 3];
+    assert_matches!(arr1.slice(s![0..1]).as_slice_memory_order(), Some(&[1]));
+    assert_matches!(arr1.slice(s![1..2]).as_slice_memory_order(), Some(&[2]));
+    assert_matches!(arr1.slice(s![2..3]).as_slice_memory_order(), Some(&[3]));
+    assert_matches!(arr1.slice(s![0..0]).as_slice_memory_order(), Some(&[]));
+
+    let arr2 = array![[1, 2, 3], [4, 5, 6]];
+    assert_matches!(arr2.slice(s![.., 2..3]).as_slice_memory_order(), None);
+    assert_matches!(arr2.slice(s![1, 2..3]).as_slice_memory_order(), Some(&[6]));
+}
+
 #[test]
 fn test_contiguous_neg_strides() {
     let s = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
diff --git a/tests/iterators.rs b/tests/iterators.rs
index d7f7c582..addd59ad 100644
--- a/tests/iterators.rs
+++ b/tests/iterators.rs
@@ -6,7 +6,7 @@
 )]
 
 use ndarray::prelude::*;
-use ndarray::{arr3, aview1, indices, s, Axis, Slice, Zip};
+use ndarray::{arr3, indices, s, Slice, Zip};
 
 use itertools::assert_equal;
 use itertools::enumerate;