Open
Description
I've been thinking about the dimension types for a while. Two things I don't like about the current implementation are that:
- Strides are represented as
usize
and have to be converted toisize
every time they're used. This is error-prone and confusing. - It seems a little weird that the dimension type itself (instead of an associated type) has a representation. For example, "2-D" doesn't necessary imply a specific representation to me.
What I'd like to do is something like the following:
pub trait Dimension
where
for<'a> Into<Self::OwnedUsize> for &'a Self::BorrowedUsize,
for<'a> Into<Self::OwnedIsize> for &'a Self::BorrowedIsize,
{
type OwnedUsize: AsRef<Self::BorrowedUsize> + AsMut<Self::BorrowedUsize> + AsRef<[usize]> + AsMut<[usize]>;
type BorrowedUsize: ?Sized + AsRef<[usize]> + AsMut<[usize]>;
type OwnedIsize: AsRef<Self::BorrowedIsize> + AsMut<Self::BorrowedIsize> + AsRef<[isize]> + AsMut<[isize]>;
type BorrowedIsize: ?Sized + AsRef<[isize]> + AsMut<[isize]>;
}
pub struct Ix2;
pub struct IxDyn;
impl Dimension for Ix2 {
type OwnedUsize = [usize; 2];
type BorrowedUsize = [usize; 2];
type OwnedIsize = [isize; 2];
type BorrowedIsize = [isize; 2];
}
impl Dimension for IxDyn {
type OwnedUsize = IxDynImpl<usize>;
type BorrowedUsize = [usize];
type OwnedIsize = IxDynImpl<isize>;
type BorrowedIsize = [isize];
}
pub trait IntoDimOwnedUsize {
type Dim: Dimension;
fn into_dim_owned_usize(self) -> Self::Dim::OwnedUsize;
}
pub trait AsDimBorrowedUsize {
type Dim: Dimension;
fn as_dim_borrowed_usize(&self) -> &Self::Dim::BorrowedUsize;
}
pub trait IntoDimOwnedIsize { ... }
pub trait AsDimBorrowedIsize { ... }
pub struct ArrayBase<S, D>
where
S: Data,
{
data: S,
ptr: *mut S::Elem,
dim: D::OwnedUsize,
strides: D::OwnedIsize,
}
impl<A, S, D> ArrayBase<S, D>
where
S: Data<Elem = A>,
D: Dimension,
{
pub fn shape(&self) -> &D::BorrowedUsize {
// ...
}
pub fn strides(&self) -> &D::BorrowedIsize {
// ...
}
}
Once Rust has generic associated types, we can simplify this to:
pub trait Dimension
where
for<'a, T: Clone> Into<Self::Owned<T>> for &'a Self::Borrowed,
{
type Owned<T>: AsRef<Self::Borrowed<T>> + AsMut<Self::Borrowed<T>> + AsRef<[T]> + AsMut<[T]>;
type Borrowed<T>>: ?Sized + AsRef<[T]> + AsMut<[T]>;
}
pub struct Ix2;
pub struct IxDyn;
impl Dimension for Ix2 {
type Owned<T> = [T; 2];
type Borrowed<T> = [T; 2];
}
impl Dimension for IxDyn {
type Owned<T> = IxDynImpl<T>;
type Borrowed<T> = [T];
}
pub trait IntoDimOwned<T> {
type Dim: Dimension;
fn into_dim_owned(self) -> Self::Dim::Owned<T>;
}
pub trait AsDimBorrowed<T> {
type Dim: Dimension;
fn as_dim_borrowed(&self) -> &Self::Dim::Borrowed<T>;
}
pub struct ArrayBase<S, D>
where
S: Data,
{
data: S,
ptr: *mut S::Elem,
dim: D::Owned<usize>,
strides: D::Owned<isize>,
}
impl<A, S, D> ArrayBase<S, D>
where
S: Data<Elem = A>,
D: Dimension,
{
pub fn shape(&self) -> &D::Borrowed<usize> {
// ...
}
pub fn strides(&self) -> &D::Borrowed<isize> {
// ...
}
}
I'd also add various type-level arithmetic operations on the dimension types, which are necessary for things like co-broadcasting (trait PartialOrdDim
) and fold_axes
(trait SubDim
).
We can also add Shape<T>
, Strides<T>
, and Index<T>
thin wrapper types.
This approach would resolve things like #489 and this comment on #367.
Thoughts?
Metadata
Metadata
Assignees
Labels
No labels