diff --git a/src/lib.rs b/src/lib.rs index 98f04bd4c..129bb0979 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -278,6 +278,31 @@ pub type Ixs = isize; /// The `ArrayBase` is parameterized by `S` for the data container and /// `D` for the dimensionality. /// +/// A third parameter, `A`, names the element type and defaults to `S::Elem`, +/// so it can usually be left out. The exception is a struct of your own with +/// an `ArrayBase` field: the compiler cannot see through the projection +/// `S::Elem` when computing the struct's variance, so leaving `A` defaulted +/// makes the struct invariant over `S` and any lifetimes in it. Spell out `A` +/// to keep the field covariant (the type aliases below already do this): +/// +/// ``` +/// use ndarray::{ArrayBase, Ix1, RawData, ViewRepr}; +/// +/// struct Wrapper> +/// { +/// data: ArrayBase, +/// } +/// +/// // Compiles only because `Wrapper` is covariant over `S`. +/// fn shorten<'a: 'b, 'b>(w: Wrapper>) -> Wrapper> +/// { +/// w +/// } +/// # +/// # let a = ndarray::arr1(&[1., 2.]); +/// # assert_eq!(shorten(Wrapper { data: a.view() }).data[1], 2.); +/// ``` +/// /// Type aliases [`Array`], [`ArcArray`], [`CowArray`], [`ArrayView`], and /// [`ArrayViewMut`] refer to `ArrayBase` with different types for the data /// container: arrays with different kinds of ownership or different kinds of array views. @@ -1574,7 +1599,7 @@ pub struct RawRef(LayoutRef); /// /// + [Constructor Methods for Owned Arrays](ArrayBase#constructor-methods-for-owned-arrays) /// + [Methods For All Array Types](ArrayBase#methods-for-all-array-types) -pub type ArcArray = ArrayBase, D>; +pub type ArcArray = ArrayBase, D, A>; /// An array that owns its data uniquely. /// @@ -1597,7 +1622,7 @@ pub type ArcArray = ArrayBase, D>; /// [`Array3`], ..., /// [`ArrayD`], /// and so on. -pub type Array = ArrayBase, D>; +pub type Array = ArrayBase, D, A>; /// An array with copy-on-write behavior. /// @@ -1615,7 +1640,7 @@ pub type Array = ArrayBase, D>; /// See also [`ArcArray`], which also provides /// copy-on-write behavior but has a reference-counted pointer to the data /// instead of either a view or a uniquely owned copy. -pub type CowArray<'a, A, D> = ArrayBase, D>; +pub type CowArray<'a, A, D> = ArrayBase, D, A>; /// A read-only array view. /// @@ -1628,7 +1653,7 @@ pub type CowArray<'a, A, D> = ArrayBase, D>; /// Array views have all the methods of an array (see [`ArrayBase`]). /// /// See also [`ArrayViewMut`]. -pub type ArrayView<'a, A, D> = ArrayBase, D>; +pub type ArrayView<'a, A, D> = ArrayBase, D, A>; /// A read-write array view. /// @@ -1641,7 +1666,7 @@ pub type ArrayView<'a, A, D> = ArrayBase, D>; /// Array views have all the methods of an array (see [`ArrayBase`]). /// /// See also [`ArrayView`]. -pub type ArrayViewMut<'a, A, D> = ArrayBase, D>; +pub type ArrayViewMut<'a, A, D> = ArrayBase, D, A>; /// A read-only array view without a lifetime. /// @@ -1664,7 +1689,7 @@ pub type ArrayViewMut<'a, A, D> = ArrayBase, D>; /// /// You can't use this type with an arbitrary raw pointer; see /// [`from_shape_ptr`](#method.from_shape_ptr) for details. -pub type RawArrayView = ArrayBase, D>; +pub type RawArrayView = ArrayBase, D, A>; /// A mutable array view without a lifetime. /// @@ -1687,7 +1712,7 @@ pub type RawArrayView = ArrayBase, D>; /// /// You can't use this type with an arbitrary raw pointer; see /// [`from_shape_ptr`](#method.from_shape_ptr) for details. -pub type RawArrayViewMut = ArrayBase, D>; +pub type RawArrayViewMut = ArrayBase, D, A>; pub use data_repr::OwnedRepr; diff --git a/tests/variance.rs b/tests/variance.rs index e72805ff7..dd26b29f2 100644 --- a/tests/variance.rs +++ b/tests/variance.rs @@ -1,4 +1,4 @@ -use ndarray::{Array1, ArrayView1}; +use ndarray::{arr1, ArcArray1, Array1, ArrayView1, ArrayViewMut1, CowArray, Ix1, RawArrayView}; fn arrayview_covariant<'a: 'b, 'b>(x: ArrayView1<'a, f64>) -> ArrayView1<'b, f64> { @@ -12,3 +12,80 @@ fn test_covariance() let shorter_view = arrayview_covariant(x.view()); assert_eq!(shorter_view[0], 0.0); } + +// The aliases must stay covariant when used as struct fields (#1610). Struct +// variance is computed from field types without normalizing projections, so an +// alias that leaves `A` to its `::Elem` default would make the +// containing struct invariant (rust-lang/rust#115799). +// +// Each `shorten_*` function compiles only if the struct is covariant. +// `RawArrayViewMut` is omitted: like `*mut T`, it is correctly invariant. + +struct HoldsView<'a>(ArrayView1<'a, f64>); + +fn shorten_view<'a: 'b, 'b>(x: HoldsView<'a>) -> HoldsView<'b> +{ + x +} + +struct HoldsViewMut<'a>(ArrayViewMut1<'a, f64>); + +fn shorten_view_mut<'a: 'b, 'b>(x: HoldsViewMut<'a>) -> HoldsViewMut<'b> +{ + x +} + +struct HoldsCow<'a>(CowArray<'a, f64, Ix1>); + +fn shorten_cow<'a: 'b, 'b>(x: HoldsCow<'a>) -> HoldsCow<'b> +{ + x +} + +struct HoldsViewOfRefs<'a>(ArrayView1<'a, &'a str>); + +fn shorten_view_of_refs<'a: 'b, 'b>(x: HoldsViewOfRefs<'a>) -> HoldsViewOfRefs<'b> +{ + x +} + +struct HoldsArray(Array1); + +fn shorten_array<'a: 'b, 'b>(x: HoldsArray<&'a str>) -> HoldsArray<&'b str> +{ + x +} + +struct HoldsArcArray(ArcArray1); + +fn shorten_arc_array<'a: 'b, 'b>(x: HoldsArcArray<&'a str>) -> HoldsArcArray<&'b str> +{ + x +} + +struct HoldsRawView(RawArrayView); + +fn shorten_raw_view<'a: 'b, 'b>(x: HoldsRawView<&'a str>) -> HoldsRawView<&'b str> +{ + x +} + +#[test] +fn test_covariance_in_struct() +{ + let mut a = arr1(&[1., 2.]); + assert_eq!(shorten_view(HoldsView(a.view())).0[0], 1.); + assert_eq!(shorten_cow(HoldsCow(CowArray::from(a.view()))).0[1], 2.); + { + let HoldsViewMut(mut v) = shorten_view_mut(HoldsViewMut(a.view_mut())); + v[0] = 3.; + } + assert_eq!(a[0], 3.); + + let s = String::from("hello"); + let strs = arr1(&[s.as_str()]); + assert_eq!(shorten_view_of_refs(HoldsViewOfRefs(strs.view())).0[0], "hello"); + assert_eq!(shorten_array(HoldsArray(strs.clone())).0[0], "hello"); + assert_eq!(shorten_arc_array(HoldsArcArray(strs.clone().into_shared())).0[0], "hello"); + assert_eq!(shorten_raw_view(HoldsRawView(strs.raw_view())).0.len(), 1); +}