diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs index ffd571977..c45799e5f 100644 --- a/src/impl_owned_array.rs +++ b/src/impl_owned_array.rs @@ -1,5 +1,44 @@ use imp_prelude::*; +/// Methods specific to `Array0`. +/// +/// ***See also all methods for [`ArrayBase`]*** +/// +/// [`ArrayBase`]: struct.ArrayBase.html +impl Array { + /// Returns the single element in the array without cloning it. + /// + /// ``` + /// use ndarray::{arr0, Array0}; + /// + /// // `Foo` doesn't implement `Clone`. + /// #[derive(Debug, Eq, PartialEq)] + /// struct Foo; + /// + /// let array: Array0 = arr0(Foo); + /// let scalar: Foo = array.into_scalar(); + /// assert_eq!(scalar, Foo); + /// ``` + pub fn into_scalar(mut self) -> A { + let size = ::std::mem::size_of::(); + if size == 0 { + // Any index in the `Vec` is fine since all elements are identical. + self.data.0.remove(0) + } else { + // Find the index in the `Vec` corresponding to `self.ptr`. + // (This is necessary because the element in the array might not be + // the first element in the `Vec`, such as if the array was created + // by `array![1, 2, 3, 4].slice_move(s![2])`.) + let first = self.ptr as usize; + let base = self.data.0.as_ptr() as usize; + let index = (first - base) / size; + debug_assert_eq!((first - base) % size, 0); + // Remove the element at the index and return it. + self.data.0.remove(index) + } + } +} + /// Methods specific to `Array`. /// /// ***See also all methods for [`ArrayBase`]*** diff --git a/src/lib.rs b/src/lib.rs index 5bf56674b..ed60d55d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -855,11 +855,11 @@ pub type Ixs = isize; /// /// /// -/// ### Conversions Between Arrays and `Vec`s/Slices +/// ### Conversions Between Arrays and `Vec`s/Slices/Scalars /// -/// This is a table of the safe conversions between arrays and `Vec`s/slices. -/// Note that some of the return values are actually `Result`/`Option` wrappers -/// around the indicated output types. +/// This is a table of the safe conversions between arrays and +/// `Vec`s/slices/scalars. Note that some of the return values are actually +/// `Result`/`Option` wrappers around the indicated output types. /// /// Input | Output | Methods /// ------|--------|-------- @@ -875,6 +875,7 @@ pub type Ixs = isize; /// `&mut ArrayBase` | `&mut [A]` | [`.as_slice_mut()`](#method.as_slice_mut)[2](#req_contig_std), [`.as_slice_memory_order_mut()`](#method.as_slice_memory_order_mut)[3](#req_contig) /// `ArrayView` | `&[A]` | [`.into_slice()`](type.ArrayView.html#method.into_slice) /// `ArrayViewMut` | `&mut [A]` | [`.into_slice()`](type.ArrayViewMut.html#method.into_slice) +/// `Array0` | `A` | [`.into_scalar()`](type.Array.html#method.into_scalar) /// /// 1Returns the data in memory order. /// diff --git a/tests/array.rs b/tests/array.rs index 0621fa322..a075246f1 100644 --- a/tests/array.rs +++ b/tests/array.rs @@ -928,6 +928,21 @@ fn as_slice_memory_order() assert!(a != b, "{:?} != {:?}", a, b); } +#[test] +fn array0_into_scalar() { + // With this kind of setup, the `Array`'s pointer is not the same as the + // underlying `Vec`'s pointer. + let a: Array0 = array![4, 5, 6, 7].into_subview(Axis(0), 2); + assert_ne!(a.as_ptr(), a.into_raw_vec().as_ptr()); + // `.into_scalar()` should still work correctly. + let a: Array0 = array![4, 5, 6, 7].into_subview(Axis(0), 2); + assert_eq!(a.into_scalar(), 6); + + // It should work for zero-size elements too. + let a: Array0<()> = array![(), (), (), ()].into_subview(Axis(0), 2); + assert_eq!(a.into_scalar(), ()); +} + #[test] fn owned_array1() { let mut a = Array::from_vec(vec![1, 2, 3, 4]);