rust-ndarray / ndarray

ndarray: an N-dimensional array with array views, multidimensional slicing, and efficient operations

Home Page:https://docs.rs/ndarray/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`ArrayView` should be covariant over lifetime

patengel opened this issue · comments

Take a look at the following example:

fn reference_covariant<'a>(x: &'static f64) -> &'a f64 {
    x
}

fn arrayview_covariant<'a>(x: ArrayView1<'static, f64>) -> ArrayView1<'a, f64> {
    x
}

The first compiles fine, the second one does not compile and give the following error message:

fn arrayview_covariant<'a>(x: ArrayView1<'static, f64>) -> ArrayView1<'a, f64> {
                       -- lifetime `'a` defined here
   x
   ^ returning this value requires that `'a` must outlive `'static`

note: requirement occurs because of the type `ArrayBase<ViewRepr<&f64>, Dim<[usize; 1]>>`, which makes the generic argument `ViewRepr<&f64>` invariant
note: the struct `ArrayBase<S, D>` is invariant over the parameter `S`

The problem is caused by the usage of S::Elem inside the definition of ArrayBase.

I stumbled across this problem, since I used ArrayView deep inside a data structure which should be covariant.
As a user of ndarray I would assume that ArrayViews behave the same as Rust references, but I see fixing this bug is hard without interface changes.
Is there an easy work-around for such problems? I currently do not know of any...

No sadly it does not. The functions in the code work on references to the complete data structure, and therefore using reborrow would require to duplicate the complete data structure.

Edit: After looking at it, this is probably related to #1341

I have minimized it to this snippet: If either the Drop implementation is removed or ArrayBase does not have <S as MyTrait>::Elem and S as elements at the same time, the covariance works. Looks to me like a limitation of the Rust compiler

fn x<'s>(x: &'s ArrayBase<OwnedRepr<&'s str>>) {
    ()
}

pub struct ArrayBase<S>
where
    S: MyTrait, {
    pub data: S,
    pub ptr: <S as MyTrait>::Elem,
}

trait MyTrait {
    type Elem;
}

pub struct OwnedRepr<A>(A);

impl<A> Drop for OwnedRepr<A> {
    fn drop(&mut self) {
        todo!()
    }
}

impl<T> MyTrait for OwnedRepr<T> {
    type Elem = T;
}

fn main() {
    let elem = "abc";

    let data = OwnedRepr(elem);

    let input = ArrayBase {
        data,
        ptr: elem
    };

    x(&input);
}

put this in order after implementing ArrayRef #879, probably not going to be possible though (?) without new Rust features.