rust-ndarray / ndarray

ndarray: an N-dimensional array with array views, multidimensional slicing, and efficient operations
https://docs.rs/ndarray/
Apache License 2.0
3.53k stars 296 forks source link

Add lifetime-preserving version of iterators #1232

Open RReverser opened 1 year ago

RReverser commented 1 year ago

Currently, ArrayView has a helpful lifetime-preserving methods like to_slice, but no way to preserve lifetimes of iterators like axis_iter, outer_iter and such.

This makes it difficult to return iterables from helper functions like this:

fn lazy_process(&self) -> impl '_ + Serialize {
    IterSerialize(self.data.outer_iter().map(move |column| {
        IterSerialize(column.outer_iter().map(|pixel| pixel.to_slice().unwrap())) // <-- cannot return value referencing function parameter `column`
    }))
}

I suggest there should be overrides like into_outer_iter, into_axis_iter etc. on ArrayView that would return iterators tied by the lifetime to the original data instead of the ArrayView itself.

Note: while looking for open issues, the only relevant I found was https://github.com/rust-ndarray/ndarray/issues/320, but there the author could afford to just reshape / flatten the data; that might not always be possible, like in my example above. @bluss mentioned issues and suggestions similar to what I'm describing here though: https://github.com/rust-ndarray/ndarray/issues/320#issuecomment-306875751

RReverser commented 1 year ago

For the example above for now found a workaround like this but it's pretty ugly compared to what it could be:

IterSerialize(
    (0..self.data.len_of(ndarray::Axis(0))).map(move |column_i| {
        IterSerialize((0..self.data.len_of(ndarray::Axis(1))).map(move |row_i| {
            self.data
                .slice(ndarray::s![column_i, row_i, ..])
                .to_slice()
                .unwrap()
        }))
    }),
)