Here's a pattern that frequently arises in my code. For instance, this thing resembling a matrix:
pub type Basis<T> = RawBasis<Vec<T>>;
pub type Slice<'a, T: 'a> = RawBasis<&'a [T]>;
// not pictured: SliceMut
#[derive(Debug, Copy, Clone)]
pub struct RawBasis<V> {
dim: usize, // width of each row
data: V,
}
impl<T> Basis<T> {
pub fn as_ref(&self) -> Slice<T> {
let RawBasis { dim, ref data } = *self;
RawBasis { dim, data: &data[..] }
}
}
Or how about this struct of arrays:
pub struct Soa(Vec<u8>, Vec<f64>);
pub struct Slice<'a>(&'a [u8], &'a [f64]);
impl Soa {
pub fn as_ref(&self) -> Slice {
Slice(&self.0[..], &self.1[..])
}
}
Basically, what happens is that I have a type like Basis
or Soa
, and methods implemented on that type; but then I find that I want to use these methods on borrowed data. So I create Slice
and SliceMut
types, move the methods over accordingly... and then I have to add a bunch a bunch of delegated stubs for the other ownership variants. I want to reduce this boilerplate somehow.
use ::std::slice;
// example of a method I probably want
pub type Iter<'a, T> = slice::Chunks<'a, T>;
impl<'a, T> Slice<'a, T> {
pub fn iter(&self) -> Iter<'a, T> { self.data.chunks(self.dim) }
}
// well, shucks, now I gotta write wrappers for the other ownership variants.
impl<T> Basis<T> {
pub fn iter(&self) -> Iter<T> { self.as_ref().iter() }
}
I've had some crazy ideas. Like, in the case of Basis
, maybe I can write this as a DST. Then Deref
would give me all of the good stuff for free!
...it is shortly after this point that I realize I have no idea where to even begin.
pub type Basis<T> = Box<RawBasis<T>>
pub type Slice<'a, T> = &'a RawBasis<T>
pub struct RawBasis<T> {
dim: usize,
data: [T],
}
impl Basis<T: Clone> {
fn filled(fill: T, dim: usize, rank: usize) -> Basis<T> {
// okay, just gotta make a Box<RawBasis<T>>
// whose data resembles vec![fill; dim * rank]
// uh.......
// uh.............
}
}