Repository: rust-ndarray/ndarray (rust)

Issue:
`ndarray` iterators don't always work well with `impl Iterator`
When writing some iterator helper functions around the base `ndarray` iterators, I found a scenario where the borrow checker rejects what seems to be valid code. The scenario is generating an iterator over indices matching some criteria in an `Array` of references.

```rust
use ndarray::{iter::IndexedIter, Array1, Ix1};

/// This doesn't compile:
///
/// > hidden type for `impl Iterator<Item = usize> + 'a` captures lifetime that does not appear in bounds
/// > hidden type ... captures the lifetime `'s` as defined here
///
/// Various alternatives I tried which have the same issue:
/// - `.iter().enumerate()` instead of `.indexed_iter()`
/// - `.filter_map()` instead of `.filter().map()`
pub fn nd_iter_non_empty_indices_not_compiling<'s, 'a>(
    array: &'a Array1<&'s str>,
) -> impl Iterator<Item = usize> + 'a {
    array
        .indexed_iter()
        .filter(|(_index, elem)| !elem.is_empty())
        .map(|(index, _elem)| index)
}

/// Adding `'a: 's` makes the function compile, but now code using it doesn't
/// compile because the constraint `'a: 's` is impossible: the container can't
/// outlive the contained elements.
///
/// ```
/// use ndarray::array;
/// use broken_iter::*;
/// let arr = array!["", "abc", "", "123"];
/// assert_eq!(nd_iter_non_empty_indices_broken(&arr).collect::<Vec<_>>(), vec![1, 3]);
/// ```
pub fn nd_iter_non_empty_indices_broken<'s, 'a: 's>(
    array: &'a Array1<&'s str>,
) -> impl Iterator<Item = usize> + 'a {
    array
        .indexed_iter()
        .filter(|(_index, elem)| !elem.is_empty())
        .map(|(index, _elem)| index)
}
```

The original problem I had was with a 2D array with more complex types and filtering operation, but the error is the same.

## Similar functions which work fine

I thought it might have been a limitation of the Rust compiler, but the same operation with a normal Rust slice (or `Vec`) works fine:

```rust
pub fn slice_iter_non_empty_indices<'s, 'a>(
    array: &'a [&'s str],
    // All of these also work:
    // array: &'a Box<[&'s str]>,
    // array: &'a Vec<&'s str>,
    // array: &'a LinkedList<&'s str>,
    // array: &'a HashSet<&'s str>,
) -> impl Iterator<Item = usize> + 'a {
    array
        .iter()
        .enumerate()
        .filter(|(_index, elem)| !elem.is_empty())
        .map(|(index, _elem)| index)
}
```

It also works if the iterator returns references:

```rust
/// A similar function which returns the `&str` elements themselves works fine.
/// ```
/// use ndarray::array;
/// use broken_iter::*;
/// let arr = array!["", "abc", "", "123"];
/// assert_eq!(nd_iter_non_empty_strings(&arr).collect::<Vec<_>>(), vec!["abc", "123"]);
/// ```
pub fn nd_iter_non_empty_strings<'s, 'a>(
    array: &'a Array1<&'s str>,
) -> impl Iterator<Item = &'s str> + 'a {
    array.iter().filter(|elem| !elem.is_empty()).cloned()
}
```

## Workaround

I am able to work around the issue by creating a custom type for the iterator, but it seems like all the extra boilerplate shouldn't be necessary, especially since it isn't required when working with `Vec`:

```rust
/// It works fine if I use a custom struct to do the filter/map.
///
/// ```
/// use ndarray::array;
/// use broken_iter::*;
/// assert_eq!(nd_iter_non_empty_indices_custom(&array!["", "abc", "", "123"]).collect::<Vec<_>>(), vec![1, 3]);
/// ```
pub fn nd_iter_non_empty_indices_custom<'s, 'a>(
    array: &'a Array1<&'s str>,
) -> NonEmptyIndices<'a, 's> {
    NonEmptyIndices {
        indexed_iter: array.indexed_iter(),
    }
}

pub struct NonEmptyIndices<'a, 's> {
    indexed_iter: IndexedIter<'a, &'s str, Ix1>,
}

impl<'a, 's> Iterator for NonEmptyIndices<'a, 's> {
    type Item = usize;

    fn next(&mut self) -> Option<Self::Item> {
        // Manual loop for the filter/map operation
        loop {
            let (index, elem) = self.indexed_iter.next()?;
            if !elem.is_empty() {
                return Some(index);
            }
        }
    }
}
```

EDIT:

I found a much better work-around by specifying a more concrete return type. (Also changed it to `filter_map` to simplify the type).

```rust
pub fn nd_iter_non_empty_indices_better<'s, 'a>(
    array: &'a Array1<&'s str>,
) -> FilterMap<
    IndexedIter<'a, &'s str, Ix1>,
    impl FnMut((usize, &'a &'s str)) -> Option<usize>,
> {
    array.indexed_iter().filter_map(
        |(index, elem)| {
            if !elem.is_empty() {
                Some(index)
            } else {
                None
            }
        },
    )
}
```

This isn't terrible to work with, but it's still weird that all of the types in `std::collections` work fine with `impl Iterator<Item = usize> + 'a` but `ndarray` types need the explicit return type.

Pull request:
Make iterators covariant in element type
The internal Baseiter type underlies most of the ndarray iterators, and it used `*mut A` for element type A. Update it to use `NonNull<A>` which behaves identically except it's guaranteed to be non-null and is covariant w.r.t the parameter A.

Add compile test from the issue.
The second commit continues the "conversion", using NonNull more consistently
in Baseiter's Iterator impl.

Fixes #1290

Patch:
diff --git a/src/data_repr.rs b/src/data_repr.rs
index a24cd778..4041c192 100644
--- a/src/data_repr.rs
+++ b/src/data_repr.rs
@@ -59,11 +59,6 @@ impl<A> OwnedRepr<A>
         self.ptr.as_ptr()
     }
 
-    pub(crate) fn as_ptr_mut(&self) -> *mut A
-    {
-        self.ptr.as_ptr()
-    }
-
     pub(crate) fn as_nonnull_mut(&mut self) -> NonNull<A>
     {
         self.ptr
diff --git a/src/impl_owned_array.rs b/src/impl_owned_array.rs
index db176210..44ac12dd 100644
--- a/src/impl_owned_array.rs
+++ b/src/impl_owned_array.rs
@@ -1,9 +1,10 @@
 #[cfg(not(feature = "std"))]
 use alloc::vec::Vec;
+use core::ptr::NonNull;
 use std::mem;
 use std::mem::MaybeUninit;
 
-#[allow(unused_imports)]
+#[allow(unused_imports)] // Needed for Rust 1.64
 use rawpointer::PointerExt;
 
 use crate::imp_prelude::*;
@@ -435,7 +436,7 @@ where D: Dimension
         // "deconstruct" self; the owned repr releases ownership of all elements and we
         // carry on with raw view methods
         let data_len = self.data.len();
-        let data_ptr = self.data.as_nonnull_mut().as_ptr();
+        let data_ptr = self.data.as_nonnull_mut();
 
         unsafe {
             // Safety: self.data releases ownership of the elements. Any panics below this point
@@ -866,8 +867,9 @@ where D: Dimension
 ///
 /// This is an internal function for use by move_into and IntoIter only, safety invariants may need
 /// to be upheld across the calls from those implementations.
-pub(crate) unsafe fn drop_unreachable_raw<A, D>(mut self_: RawArrayViewMut<A, D>, data_ptr: *mut A, data_len: usize)
-where D: Dimension
+pub(crate) unsafe fn drop_unreachable_raw<A, D>(
+    mut self_: RawArrayViewMut<A, D>, data_ptr: NonNull<A>, data_len: usize,
+) where D: Dimension
 {
     let self_len = self_.len();
 
@@ -878,7 +880,7 @@ where D: Dimension
     }
     sort_axes_in_default_order(&mut self_);
     // with uninverted axes this is now the element with lowest address
-    let array_memory_head_ptr = self_.ptr.as_ptr();
+    let array_memory_head_ptr = self_.ptr;
     let data_end_ptr = data_ptr.add(data_len);
     debug_assert!(data_ptr <= array_memory_head_ptr);
     debug_assert!(array_memory_head_ptr <= data_end_ptr);
@@ -907,7 +909,7 @@ where D: Dimension
 
     // iter is a raw pointer iterator traversing the array in memory order now with the
     // sorted axes.
-    let mut iter = Baseiter::new(self_.ptr.as_ptr(), self_.dim, self_.strides);
+    let mut iter = Baseiter::new(self_.ptr, self_.dim, self_.strides);
     let mut dropped_elements = 0;
 
     let mut last_ptr = data_ptr;
@@ -917,7 +919,7 @@ where D: Dimension
         // should now be dropped. This interval may be empty, then we just skip this loop.
         while last_ptr != elem_ptr {
             debug_assert!(last_ptr < data_end_ptr);
-            std::ptr::drop_in_place(last_ptr);
+            std::ptr::drop_in_place(last_ptr.as_mut());
             last_ptr = last_ptr.add(1);
             dropped_elements += 1;
         }
@@ -926,7 +928,7 @@ where D: Dimension
     }
 
     while last_ptr < data_end_ptr {
-        std::ptr::drop_in_place(last_ptr);
+        std::ptr::drop_in_place(last_ptr.as_mut());
         last_ptr = last_ptr.add(1);
         dropped_elements += 1;
     }
diff --git a/src/impl_views/conversions.rs b/src/impl_views/conversions.rs
index ef6923a5..1dd7d97f 100644
--- a/src/impl_views/conversions.rs
+++ b/src/impl_views/conversions.rs
@@ -199,7 +199,7 @@ where D: Dimension
     #[inline]
     pub(crate) fn into_base_iter(self) -> Baseiter<A, D>
     {
-        unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) }
+        unsafe { Baseiter::new(self.ptr, self.dim, self.strides) }
     }
 }
 
@@ -209,7 +209,7 @@ where D: Dimension
     #[inline]
     pub(crate) fn into_base_iter(self) -> Baseiter<A, D>
     {
-        unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) }
+        unsafe { Baseiter::new(self.ptr, self.dim, self.strides) }
     }
 }
 
@@ -220,7 +220,7 @@ where D: Dimension
     #[inline]
     pub(crate) fn into_base_iter(self) -> Baseiter<A, D>
     {
-        unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) }
+        unsafe { Baseiter::new(self.ptr, self.dim, self.strides) }
     }
 
     #[inline]
@@ -262,7 +262,7 @@ where D: Dimension
     #[inline]
     pub(crate) fn into_base_iter(self) -> Baseiter<A, D>
     {
-        unsafe { Baseiter::new(self.ptr.as_ptr(), self.dim, self.strides) }
+        unsafe { Baseiter::new(self.ptr, self.dim, self.strides) }
     }
 
     #[inline]
diff --git a/src/iterators/chunks.rs b/src/iterators/chunks.rs
index 46542896..9e2f08e1 100644
--- a/src/iterators/chunks.rs
+++ b/src/iterators/chunks.rs
@@ -204,7 +204,7 @@ impl_iterator! {
 
         fn item(&mut self, ptr) {
             unsafe {
-                ArrayView::new_(
+                ArrayView::new(
                     ptr,
                     self.chunk.clone(),
                     self.inner_strides.clone())
@@ -226,7 +226,7 @@ impl_iterator! {
 
         fn item(&mut self, ptr) {
             unsafe {
-                ArrayViewMut::new_(
+                ArrayViewMut::new(
                     ptr,
                     self.chunk.clone(),
                     self.inner_strides.clone())
diff --git a/src/iterators/into_iter.rs b/src/iterators/into_iter.rs
index fcc2e4b8..9374608c 100644
--- a/src/iterators/into_iter.rs
+++ b/src/iterators/into_iter.rs
@@ -33,16 +33,15 @@ impl<A, D> IntoIter<A, D>
 where D: Dimension
 {
     /// Create a new by-value iterator that consumes `array`
-    pub(crate) fn new(mut array: Array<A, D>) -> Self
+    pub(crate) fn new(array: Array<A, D>) -> Self
     {
         unsafe {
             let array_head_ptr = array.ptr;
-            let ptr = array.as_mut_ptr();
             let mut array_data = array.data;
             let data_len = array_data.release_all_elements();
             debug_assert!(data_len >= array.dim.size());
             let has_unreachable_elements = array.dim.size() != data_len;
-            let inner = Baseiter::new(ptr, array.dim, array.strides);
+            let inner = Baseiter::new(array_head_ptr, array.dim, array.strides);
 
             IntoIter {
                 array_data,
@@ -62,7 +61,7 @@ impl<A, D: Dimension> Iterator for IntoIter<A, D>
     #[inline]
     fn next(&mut self) -> Option<A>
     {
-        self.inner.next().map(|p| unsafe { p.read() })
+        self.inner.next().map(|p| unsafe { p.as_ptr().read() })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>)
@@ -92,7 +91,7 @@ where D: Dimension
         while let Some(_) = self.next() {}
 
         unsafe {
-            let data_ptr = self.array_data.as_ptr_mut();
+            let data_ptr = self.array_data.as_nonnull_mut();
             let view = RawArrayViewMut::new(self.array_head_ptr, self.inner.dim.clone(), self.inner.strides.clone());
             debug_assert!(self.inner.dim.size() < self.data_len, "data_len {} and dim size {}",
                           self.data_len, self.inner.dim.size());
diff --git a/src/iterators/mod.rs b/src/iterators/mod.rs
index d49ffe2d..e7321d15 100644
--- a/src/iterators/mod.rs
+++ b/src/iterators/mod.rs
@@ -19,6 +19,10 @@ use alloc::vec::Vec;
 use std::iter::FromIterator;
 use std::marker::PhantomData;
 use std::ptr;
+use std::ptr::NonNull;
+
+#[allow(unused_imports)] // Needed for Rust 1.64
+use rawpointer::PointerExt;
 
 use crate::Ix1;
 
@@ -34,11 +38,11 @@ use std::slice::{self, Iter as SliceIter, IterMut as SliceIterMut};
 
 /// Base for iterators over all axes.
 ///
-/// Iterator element type is `*mut A`.
+/// Iterator element type is `NonNull<A>`.
 #[derive(Debug)]
 pub struct Baseiter<A, D>
 {
-    ptr: *mut A,
+    ptr: NonNull<A>,
     dim: D,
     strides: D,
     index: Option<D>,
@@ -50,7 +54,7 @@ impl<A, D: Dimension> Baseiter<A, D>
     /// to be correct to avoid performing an unsafe pointer offset while
     /// iterating.
     #[inline]
-    pub unsafe fn new(ptr: *mut A, len: D, stride: D) -> Baseiter<A, D>
+    pub unsafe fn new(ptr: NonNull<A>, len: D, stride: D) -> Baseiter<A, D>
     {
         Baseiter {
             ptr,
@@ -63,10 +67,10 @@ impl<A, D: Dimension> Baseiter<A, D>
 
 impl<A, D: Dimension> Iterator for Baseiter<A, D>
 {
-    type Item = *mut A;
+    type Item = NonNull<A>;
 
     #[inline]
-    fn next(&mut self) -> Option<*mut A>
+    fn next(&mut self) -> Option<Self::Item>
     {
         let index = match self.index {
             None => return None,
@@ -84,7 +88,7 @@ impl<A, D: Dimension> Iterator for Baseiter<A, D>
     }
 
     fn fold<Acc, G>(mut self, init: Acc, mut g: G) -> Acc
-    where G: FnMut(Acc, *mut A) -> Acc
+    where G: FnMut(Acc, Self::Item) -> Acc
     {
         let ndim = self.dim.ndim();
         debug_assert_ne!(ndim, 0);
@@ -133,14 +137,14 @@ impl<A, D: Dimension> ExactSizeIterator for Baseiter<A, D>
 impl<A> DoubleEndedIterator for Baseiter<A, Ix1>
 {
     #[inline]
-    fn next_back(&mut self) -> Option<*mut A>
+    fn next_back(&mut self) -> Option<Self::Item>
     {
         let index = match self.index {
             None => return None,
             Some(ix) => ix,
         };
         self.dim[0] -= 1;
-        let offset = <_>::stride_offset(&self.dim, &self.strides);
+        let offset = Ix1::stride_offset(&self.dim, &self.strides);
         if index == self.dim {
             self.index = None;
         }
@@ -148,13 +152,13 @@ impl<A> DoubleEndedIterator for Baseiter<A, Ix1>
         unsafe { Some(self.ptr.offset(offset)) }
     }
 
-    fn nth_back(&mut self, n: usize) -> Option<*mut A>
+    fn nth_back(&mut self, n: usize) -> Option<Self::Item>
     {
         let index = self.index?;
         let len = self.dim[0] - index[0];
         if n < len {
             self.dim[0] -= n + 1;
-            let offset = <_>::stride_offset(&self.dim, &self.strides);
+            let offset = Ix1::stride_offset(&self.dim, &self.strides);
             if index == self.dim {
                 self.index = None;
             }
@@ -166,7 +170,7 @@ impl<A> DoubleEndedIterator for Baseiter<A, Ix1>
     }
 
     fn rfold<Acc, G>(mut self, init: Acc, mut g: G) -> Acc
-    where G: FnMut(Acc, *mut A) -> Acc
+    where G: FnMut(Acc, Self::Item) -> Acc
     {
         let mut accum = init;
         if let Some(index) = self.index {
@@ -226,7 +230,7 @@ impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D>
     #[inline]
     fn next(&mut self) -> Option<&'a A>
     {
-        self.inner.next().map(|p| unsafe { &*p })
+        self.inner.next().map(|p| unsafe { p.as_ref() })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>)
@@ -237,7 +241,7 @@ impl<'a, A, D: Dimension> Iterator for ElementsBase<'a, A, D>
     fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc
     where G: FnMut(Acc, Self::Item) -> Acc
     {
-        unsafe { self.inner.fold(init, move |acc, ptr| g(acc, &*ptr)) }
+        unsafe { self.inner.fold(init, move |acc, ptr| g(acc, ptr.as_ref())) }
     }
 }
 
@@ -246,13 +250,13 @@ impl<'a, A> DoubleEndedIterator for ElementsBase<'a, A, Ix1>
     #[inline]
     fn next_back(&mut self) -> Option<&'a A>
     {
-        self.inner.next_back().map(|p| unsafe { &*p })
+        self.inner.next_back().map(|p| unsafe { p.as_ref() })
     }
 
     fn rfold<Acc, G>(self, init: Acc, mut g: G) -> Acc
     where G: FnMut(Acc, Self::Item) -> Acc
     {
-        unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, &*ptr)) }
+        unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, ptr.as_ref())) }
     }
 }
 
@@ -646,7 +650,7 @@ impl<'a, A, D: Dimension> Iterator for ElementsBaseMut<'a, A, D>
     #[inline]
     fn next(&mut self) -> Option<&'a mut A>
     {
-        self.inner.next().map(|p| unsafe { &mut *p })
+        self.inner.next().map(|mut p| unsafe { p.as_mut() })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>)
@@ -657,7 +661,10 @@ impl<'a, A, D: Dimension> Iterator for ElementsBaseMut<'a, A, D>
     fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc
     where G: FnMut(Acc, Self::Item) -> Acc
     {
-        unsafe { self.inner.fold(init, move |acc, ptr| g(acc, &mut *ptr)) }
+        unsafe {
+            self.inner
+                .fold(init, move |acc, mut ptr| g(acc, ptr.as_mut()))
+        }
     }
 }
 
@@ -666,13 +673,16 @@ impl<'a, A> DoubleEndedIterator for ElementsBaseMut<'a, A, Ix1>
     #[inline]
     fn next_back(&mut self) -> Option<&'a mut A>
     {
-        self.inner.next_back().map(|p| unsafe { &mut *p })
+        self.inner.next_back().map(|mut p| unsafe { p.as_mut() })
     }
 
     fn rfold<Acc, G>(self, init: Acc, mut g: G) -> Acc
     where G: FnMut(Acc, Self::Item) -> Acc
     {
-        unsafe { self.inner.rfold(init, move |acc, ptr| g(acc, &mut *ptr)) }
+        unsafe {
+            self.inner
+                .rfold(init, move |acc, mut ptr| g(acc, ptr.as_mut()))
+        }
     }
 }
 
@@ -748,7 +758,7 @@ where D: Dimension
     {
         self.iter
             .next()
-            .map(|ptr| unsafe { ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) })
+            .map(|ptr| unsafe { ArrayView::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>)
@@ -772,7 +782,7 @@ impl<'a, A> DoubleEndedIterator for LanesIter<'a, A, Ix1>
     {
         self.iter
             .next_back()
-            .map(|ptr| unsafe { ArrayView::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) })
+            .map(|ptr| unsafe { ArrayView::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) })
     }
 }
 
@@ -800,7 +810,7 @@ where D: Dimension
     {
         self.iter
             .next()
-            .map(|ptr| unsafe { ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) })
+            .map(|ptr| unsafe { ArrayViewMut::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) })
     }
 
     fn size_hint(&self) -> (usize, Option<usize>)
@@ -824,7 +834,7 @@ impl<'a, A> DoubleEndedIterator for LanesIterMut<'a, A, Ix1>
     {
         self.iter
             .next_back()
-            .map(|ptr| unsafe { ArrayViewMut::new_(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) })
+            .map(|ptr| unsafe { ArrayViewMut::new(ptr, Ix1(self.inner_len), Ix1(self.inner_stride as Ix)) })
     }
 }
 
diff --git a/src/iterators/windows.rs b/src/iterators/windows.rs
index 453ef502..1c2ab6a8 100644
--- a/src/iterators/windows.rs
+++ b/src/iterators/windows.rs
@@ -115,7 +115,7 @@ impl_iterator! {
 
         fn item(&mut self, ptr) {
             unsafe {
-                ArrayView::new_(
+                ArrayView::new(
                     ptr,
                     self.window.clone(),
                     self.strides.clone())


Test patch:
diff --git a/tests/iterators.rs b/tests/iterators.rs
index 23175fd4..908b64d1 100644
--- a/tests/iterators.rs
+++ b/tests/iterators.rs
@@ -1,6 +1,4 @@
-#![allow(
-    clippy::many_single_char_names, clippy::deref_addrof, clippy::unreadable_literal, clippy::many_single_char_names
-)]
+#![allow(clippy::deref_addrof, clippy::unreadable_literal)]
 
 use ndarray::prelude::*;
 use ndarray::{arr3, indices, s, Slice, Zip};
@@ -1055,3 +1053,33 @@ impl Drop for DropCount<'_>
         self.drops.set(self.drops.get() + 1);
     }
 }
+
+#[test]
+fn test_impl_iter_compiles()
+{
+    // Requires that the iterators are covariant in the element type
+
+    // base case: std
+    fn slice_iter_non_empty_indices<'s, 'a>(array: &'a Vec<&'s str>) -> impl Iterator<Item = usize> + 'a
+    {
+        array
+            .iter()
+            .enumerate()
+            .filter(|(_index, elem)| !elem.is_empty())
+            .map(|(index, _elem)| index)
+    }
+
+    let _ = slice_iter_non_empty_indices;
+
+    // ndarray case
+    fn array_iter_non_empty_indices<'s, 'a>(array: &'a Array<&'s str, Ix1>) -> impl Iterator<Item = usize> + 'a
+    {
+        array
+            .iter()
+            .enumerate()
+            .filter(|(_index, elem)| !elem.is_empty())
+            .map(|(index, _elem)| index)
+    }
+
+    let _ = array_iter_non_empty_indices;
+}