-
Notifications
You must be signed in to change notification settings - Fork 307
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Lifetime issues with slice
methods
#1208
Comments
The existing impl<A, S, D> ArrayBase<S, D>
where
S: RawData<Elem = A>,
D: Dimension,
{
pub fn slice_axis(&self, axis: Axis, indices: Slice) -> ArrayView<'_, A, D>
where
S: Data,
{ ... }
} With explicit lifetimes, that's equivalent to: impl<A, S, D> ArrayBase<S, D>
where
S: RawData<Elem = A>,
D: Dimension,
{
pub fn slice_axis<'b>(&'b self, axis: Axis, indices: Slice) -> ArrayView<'b, A, D>
where
S: Data,
{ ... }
} It's defined this way in order to be generic over all storage types (owned arrays, views, mutable views, etc.).
pub type ArrayView<'a, A, D> = ArrayBase<ViewRepr<&'a A>, D>; Let's call In the case of impl<'a, A, D> ArrayView<'a, A, D>
where
D: Dimension,
{
pub fn slice_axis<'b>(&'b self, axis: Axis, indices: Slice) -> ArrayView<'b, A, D> { ... }
} The "data" lifetime of the returned view corresponds to the borrow of fn foo<'a, T, D>(arrayview: ArrayView<'a, T, D>, axis: ndarray::Axis) -> ArrayView<'a, T, D>
where
D: Dimension,
{
ArrayBase::slice_axis(&arrayview, axis, Slice::from(1..2))
} With this de-sugared source, it's clearer why it fails to compile: the You are correct that we could define an additional method for impl<'a, T, D> ArrayView<'a, T, D>
where
D: Dimension,
{
pub fn slice_axis_preserve_lifetime<'b>(&'b self, axis: Axis, indices: Slice) -> ArrayView<'a, T, D> { ... }
} This differs from the existing However, an Does my explanation above answer your question? |
Woah thanks a lot for the comprehensive explanation, it's crystal clear ! Ultimately I ended up doing the same thing as you did in your implementation of However, I've been tinkering more with ndarray in the past week, and I think I would like to generalize the topic of this post a little bit. I feel like ArrayViews are not as easy to use as I would like. Conceptually, I was hoping to use them in the same way as when I use a Usage of arrayviews also feel clunky when you look at I have another example of why this approach feels clunky to me: I had to write this extension trait to be able to write functions automatically accepting references to arrays or array views: /// Let's you pass either an owned ArrayView or an immutable reference to an array to a function that needs only an array view
pub trait AsArrayView<'data, T, D>
where
D: Dimension,
{
fn view(self: Self) -> ArrayView<'data, T, D>;
}
impl<'data, T, D> AsArrayView<'data, T, D> for ArrayView<'data, T, D>
where
D: Dimension,
{
fn view(self: ArrayView<'data, T, D>) -> ArrayView<'data, T, D> {
self
}
}
impl<'data, T, D> AsArrayView<'data, T, D> for &'data Array<T, D>
where
D: Dimension,
{
fn view(self: &'data Array<T, D>) -> ArrayView<'data, T, D> {
self.view()
}
} This made it easier for me to develop code where &arrays are used interchangeably with array views when calling code that only requires an array view. (Btw I was surprised such a trait wasn't already existing in the library, maybe it's worth upsourcing something like that ?) After having worked with ndarray for a week or so, this is definitely my main pain point, ArrayViews don't feel like pointer types enough. I'm honestly not sure how this could be addressed however, I do understand the appeal of having methods being declared on ArrayBase instead, but maybe they could be specialized on the ArrayView type, if they were defined in a separate trait instead ? (mostly food for thoughts, I probably doesn't have a clue of what this would imply in terms of library architectural changes ahah) |
Fwiw, this isn't true. Calling I agree that views are much more awkward to use than Regarding the existing version of |
Oh I definitely missed this part, thanks for correcting me then. Thanks for linking the RFC, I wasn't aware of all this work you had going on. The thoughts you laid out seem really interesting, I probably don't know the project enough to have a meaningful opinion, but it does look like a step in a better direction to me. I noted that the discussions on this date back to 2021, is there a technical reason that blocked implementation or is this only linked with dev bandwidth/logistics (which are perfectly fine reasons ofc)? Is the project lacking contributors? I'm also very interested in this idea of Trait-based implementations, this looks like a much more flexible approach to adding behaviours on top of data storages. I'm currently writing a naive implementation of 1-dim masking in a separate trait to mimic numpy's capabilities here, if this proves mature enough at some point I might open a PR to share it. Anyway, thanks for all the answers Jim 🙇 |
Yes, it's basically due to dev bandwidth. @bluss and I are the primary contributors, but I don't think @bluss has much time to work on |
Hello folks,
I think I found an issue in the way lifetimes are inferred when using
slice
orslice_axis
on array views.Here is a minimal example with three functions, only
bar
andbaz
compile, whilefoo
doesn't. I would argue thatfoo
should be able to compile.(playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7d62b20eec45d257e629dae07840544b)
The error is the following:
My understanding of the problem is the following: the lifetime of the data represented by the array view passed in as argument to
foo
is not propagated correctly to the one obtained by slicing it, and the output of the slice function is actually depending on the lifetime of the view passed as argument and not the lifetime of the data referenced by the view.In
bar
I show how to circumnavigate the issue by slicing in place. Given that views are cheap to create and clone, this is the alternative I'm going with for now.In
baz
, I show a version where I explicitly annotate lifetimes to make it work, note how I have to constrain'a
to'data
and how I need to make the argument a reference to avoid having the function drop it while the returned value still references it.Also, I'm not sure why the error message is angry about the lifetime of D in the first place, I don't see where it's being defined as a reference or a pointer.
I'm not super good at understanding lifetimes but it feels like it should be possible to slice a view and obtain something that doesn't depend on the view itself but on the underlying data only. Or am I missing something here ?
The text was updated successfully, but these errors were encountered: