Skip to content

Instantly share code, notes, and snippets.

@ExpHP
Last active July 30, 2018 01:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ExpHP/811e7a650754dc87ebe5d263cf8bef4d to your computer and use it in GitHub Desktop.
Save ExpHP/811e7a650754dc87ebe5d263cf8bef4d to your computer and use it in GitHub Desktop.
crazy index polymorphism
/// Either `[X]` (for `I = usize`) or `Indexed<I, [X]>`.
pub type SliceType<I, X> = <() as IndexFamily<I, X>>::Slice;
/// Either `Vec<X>` (for `I = usize`) or `Indexed<I, Vec<X>>`.
pub type OwnedType<I, X> = <() as IndexFamily<I, X>>::Owned;
/// Can be used to retrofit support for Indexed into old vec/slice-based interfaces.
///
/// The associated types `Slice` and `Owned` are `[X]` and `Vec<X>` for `I = usize`,
/// and `Indexed<I, _>` for anything else. That makes them suitable for retrofitting
/// functions that used to return slices or vecs.
///
/// Thanks to this (and liberal use of `usize` defaults for index type parameters),
/// adoption of `Indexed` can be done incrementally.
///
/// ...the annoying bit is that it frequently needs to be written explicitly in where bounds.
pub trait IndexFamily<I: Idx, X> {
type Slice: ?Sized;
type Owned;
// For converting the output type of a method that lies at the interface between
// code generic over `I: Idx` and code that is not.
fn ref_from_indexed(slice: &Indexed<I, [X]>) -> &Self::Slice;
fn mut_from_indexed(slice: &mut Indexed<I, [X]>) -> &mut Self::Slice;
fn owned_from_indexed(vec: Indexed<I, Vec<X>>) -> Self::Owned;
// `OwnedType<I, X>` as an input arg does not work the way you might hope. (rust won't
// be able to infer `I` and `X`.) You'll have to use `impl IntoIndexed`, even though
// it doesn't constrain the input type as well as `Vec` used to.
// fn indexed_from_owned(vec: Self::Owned) -> Indexed<I, Vec<X>>;
// I can't think of why these would ever be needed. Take `impl AsIndexed`
// or `impl AsIndexedMut`, which have no foreseeable disadvantages.
// fn indexed_from_ref(slice: &Self::Slice) -> &Indexed<I, [X]>;
// fn indexed_from_mut(slice: &mut Self::Slice) -> &mut Indexed<I, [X]>;
}
impl<X> IndexFamily<usize, X> for () {
type Slice = [X];
type Owned = Vec<X>;
#[inline] fn ref_from_indexed(slice: &Indexed<usize, [X]>) -> &Self::Slice
{ &slice.raw }
#[inline] fn mut_from_indexed(slice: &mut Indexed<usize, [X]>) -> &mut Self::Slice
{ &mut slice.raw }
#[inline] fn owned_from_indexed(vec: Indexed<usize, Vec<X>>) -> Self::Owned
{ vec.raw }
// #[inline] fn indexed_from_ref(slice: &Self::Slice) -> &Indexed<usize, [X]>
// { Indexed::from_raw_ref(slice) }
// #[inline] fn indexed_from_mut(slice: &mut Self::Slice) -> &mut Indexed<usize, [X]>
// { Indexed::from_raw_mut(slice) }
// #[inline] fn indexed_from_owned(vec: Self::Owned) -> Indexed<usize, Vec<X>>
// { Indexed::from_raw(vec) }
}
/// Implemented by newtype indices to allow `IndexFamily` to distinguish them from `usize`.
///
/// **Note:** Whenever you see a type error that "I: IsNewtypeIdx is not satisfied", there is
/// literally a 0% chance that adding this bound is the solution. Some possible causes of
/// this error:
///
/// * Did you forget to add a `IndexFamily<I, X>` bound? (these bounds are required in virtually
/// one-to-one correspondence with appearances of `SliceType`/`VecType`s)
/// * Did you write `SliceType<I, [X]>` instead of `SliceType<I, X>`?
/// * If you're calling a method of a type that has an `IndexFamily` bound, and you are writing
/// code that is generic over `I: Idx`, look for a version of the method without the bound.
/// (its name might contain the word 'indexed', and it will return `Indexed` where the original
/// method returned `SliceType` or `OwnedType`. This will make your life 1000x times easier
/// than trying to work with `SliceType` or `OwnedType` in a generic context.)
pub trait IsNewtypeIdx: Idx {}
impl<I: IsNewtypeIdx, X> IndexFamily<I, X> for () {
type Slice = Indexed<I, [X]>;
type Owned = Indexed<I, Vec<X>>;
#[inline] fn ref_from_indexed(slice: &Indexed<I, [X]>) -> &Self::Slice { slice }
#[inline] fn mut_from_indexed(slice: &mut Indexed<I, [X]>) -> &mut Self::Slice { slice }
#[inline] fn owned_from_indexed(vec: Indexed<I, Vec<X>>) -> Self::Owned { vec }
// #[inline] fn indexed_from_ref(slice: &Self::Slice) -> &Indexed<I, [X]> { slice }
// #[inline] fn indexed_from_mut(slice: &mut Self::Slice) -> &mut Indexed<I, [X]> { slice }
// #[inline] fn indexed_from_owned(vec: Self::Owned) -> Indexed<I, Vec<X>> { vec }
}
//--------------------------------------------------------
impl<I: Idx, V: Deref<Target=[T]>, T> Deref for Indexed<I, V> {
type Target = Indexed<I, [T]>;
#[inline]
fn deref(&self) -> &Self::Target {
Indexed::from_raw_ref(self.raw.deref())
}
}
impl<I: Idx, V: DerefMut<Target=[T]>, T> DerefMut for Indexed<I, V> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
Indexed::from_raw_mut(self.raw.deref_mut())
}
}
//--------------------------------------------------------
/// A trait recommended for use in argument lists where you require an `&Indexed<I, [T]>`.
///
/// It converts `&[T]` into `&Indexed<usize, [T]>`, helping support legacy code, and is also
/// polymorphic over by-value versus by-ref, which is just generally nice.
///
/// Use as `impl AsIndexed<I, T>` in an argument list.
pub trait AsIndexed: HasIndexType {
type Elem;
fn as_indexed(&self) -> &Indexed<Self::Index, [Self::Elem]>;
}
/// A trait recommended for use in argument lists where you require a `&mut Indexed<I, [T]>`.
///
/// It converts `&mut [T]` into `&mut Indexed<usize, [T]>`, helping support legacy code,
/// and is also polymorphic over by-value versus by-ref, which is just generally nice.
///
/// Use as `impl AsIndexedMut<I, T>` in an argument list.
pub trait AsIndexedMut: AsIndexed {
fn as_indexed_mut(&mut self) -> &mut Indexed<Self::Index, [Self::Elem]>;
}
/// A trait recommended for use in argument lists where you require a `Indexed<I, Vec<T>>`.
///
/// It converts `Vec<T>` into `Indexed<usize, Vec<T>>`, helping support legacy code,
/// and is also polymorphic over by-value (no-op) versus by-ref (copy), which is just
/// generally nice.
///
/// Use as `impl AsIndexedMut<I, T>` in an argument list.
pub trait IntoIndexed: HasIndexType {
type Elem;
fn into_indexed(self) -> Indexed<Self::Index, Vec<Self::Elem>>;
}
impl<T> AsIndexed for [T] {
type Elem = T;
#[inline]
fn as_indexed(&self) -> &Indexed<usize, [Self::Elem]> {
Indexed::from_raw_ref(self)
}
}
impl<T> AsIndexedMut for [T] {
#[inline]
fn as_indexed_mut(&mut self) -> &mut Indexed<usize, [Self::Elem]> {
Indexed::from_raw_mut(self)
}
}
impl<'a, T> IntoIndexed for &'a [T]
where
T: Clone,
{
type Elem = T;
#[inline]
fn into_indexed(self) -> Indexed<usize, Vec<Self::Elem>> {
Indexed::from_raw(self.to_owned())
}
}
impl<'a, T> IntoIndexed for &'a Vec<T>
where
T: Clone,
{
type Elem = T;
#[inline]
fn into_indexed(self) -> Indexed<usize, Vec<Self::Elem>> {
Indexed::from_raw(self.to_owned())
}
}
impl<T> AsIndexed for Vec<T> {
type Elem = T;
#[inline]
fn as_indexed(&self) -> &Indexed<usize, [Self::Elem]> {
self[..].as_indexed()
}
}
impl<T> AsIndexedMut for Vec<T> {
#[inline]
fn as_indexed_mut(&mut self) -> &mut Indexed<usize, [Self::Elem]> {
self[..].as_indexed_mut()
}
}
impl<T> IntoIndexed for Vec<T> {
type Elem = T;
#[inline]
fn into_indexed(self) -> Indexed<usize, Vec<Self::Elem>> {
Indexed::from_raw(self)
}
}
impl<I: Idx, T> AsIndexed for Indexed<I, [T]> {
type Elem = T;
#[inline]
fn as_indexed(&self) -> &Indexed<Self::Index, [Self::Elem]> {
self
}
}
impl<I: Idx, T> AsIndexedMut for Indexed<I, [T]> {
#[inline]
fn as_indexed_mut(&mut self) -> &mut Indexed<Self::Index, [Self::Elem]> {
self
}
}
impl<'a, I: Idx, T> IntoIndexed for &'a Indexed<I, [T]>
where
T: Clone,
{
type Elem = T;
#[inline]
fn into_indexed(self) -> Indexed<Self::Index, Vec<Self::Elem>> {
Indexed::from_raw(self.raw.to_owned())
}
}
impl<'a, I: Idx, T> IntoIndexed for &'a Indexed<I, Vec<T>>
where
T: Clone,
{
type Elem = T;
#[inline]
fn into_indexed(self) -> Indexed<Self::Index, Vec<Self::Elem>> {
Indexed::from_raw(self.raw.to_owned())
}
}
impl<I: Idx, T> AsIndexed for Indexed<I, Vec<T>> {
type Elem = T;
#[inline]
fn as_indexed(&self) -> &Indexed<Self::Index, [Self::Elem]> {
self
}
}
impl<I: Idx, T> AsIndexedMut for Indexed<I, Vec<T>> {
#[inline]
fn as_indexed_mut(&mut self) -> &mut Indexed<Self::Index, [Self::Elem]> {
self
}
}
impl<I: Idx, T> IntoIndexed for Indexed<I, Vec<T>> {
type Elem = T;
#[inline]
fn into_indexed(self) -> Indexed<Self::Index, Vec<Self::Elem>> {
self
}
}
impl<'a, V: ?Sized + AsIndexed> AsIndexed for &'a V {
type Elem = V::Elem;
#[inline]
fn as_indexed(&self) -> &Indexed<Self::Index, [Self::Elem]> {
(**self).as_indexed()
}
}
impl<'a, V: ?Sized + AsIndexed> AsIndexed for &'a mut V {
type Elem = V::Elem;
#[inline]
fn as_indexed(&self) -> &Indexed<Self::Index, [Self::Elem]> {
(**self).as_indexed()
}
}
impl<'a, V: ?Sized + AsIndexedMut> AsIndexedMut for &'a mut V {
#[inline]
fn as_indexed_mut(&mut self) -> &mut Indexed<Self::Index, [Self::Elem]> {
(**self).as_indexed_mut()
}
}
// NOTE: this also supports &&&V and &&&&V and etc. through induction,
// though in practice only &&V ever shows up.
impl<'a, 'b, V: ?Sized> IntoIndexed for &'a &'b V
where &'b V: IntoIndexed,
{
type Elem = <&'b V as IntoIndexed>::Elem;
#[inline]
fn into_indexed(self) -> Indexed<Self::Index, Vec<Self::Elem>> {
(**self).into_indexed()
}
}
#[test]
fn test_impl_existence() {
newtype_index!{Foo}
fn check_as_indexed(_v: impl AsIndexed) {}
fn check_as_indexed_mut(_v: impl AsIndexedMut) {}
fn check_into_indexed(_v: impl IntoIndexed) {}
let mut vec = vec![()];
let mut indexed_vec_1: Indexed<usize, Vec<()>> = vec![()].into_iter().collect();
let mut indexed_vec_2: Indexed<Foo, Vec<()>> = vec![()].into_iter().collect();
check_as_indexed(vec.clone());
check_as_indexed(indexed_vec_1.clone());
check_as_indexed(indexed_vec_2.clone());
check_as_indexed(&vec);
check_as_indexed(&indexed_vec_1);
check_as_indexed(&indexed_vec_2);
check_as_indexed(&vec[..]);
check_as_indexed(&indexed_vec_1[..]);
check_as_indexed(&indexed_vec_2[..]);
check_as_indexed_mut(vec.clone());
check_as_indexed_mut(indexed_vec_1.clone());
check_as_indexed_mut(indexed_vec_2.clone());
check_as_indexed_mut(&mut vec);
check_as_indexed_mut(&mut indexed_vec_1);
check_as_indexed_mut(&mut indexed_vec_2);
check_as_indexed_mut(&mut vec[..]);
check_as_indexed_mut(&mut indexed_vec_1[..]);
check_as_indexed_mut(&mut indexed_vec_2[..]);
check_into_indexed(vec.clone());
check_into_indexed(indexed_vec_1.clone());
check_into_indexed(indexed_vec_2.clone());
check_into_indexed(&vec);
check_into_indexed(&indexed_vec_1);
check_into_indexed(&indexed_vec_2);
check_into_indexed(&vec[..]);
check_into_indexed(&indexed_vec_1[..]);
check_into_indexed(&indexed_vec_2[..]);
// types that frequently end up in function arguments after Extract Method refactoring...
check_as_indexed(&&vec[..]);
check_as_indexed(&&indexed_vec_1[..]);
check_as_indexed(&&indexed_vec_2[..]);
check_as_indexed_mut(&mut &mut vec[..]);
check_as_indexed_mut(&mut &mut indexed_vec_1[..]);
check_as_indexed_mut(&mut &mut indexed_vec_2[..]);
check_into_indexed(&&vec[..]);
check_into_indexed(&&indexed_vec_1[..]);
check_into_indexed(&&indexed_vec_2[..]);
}
//--------------------------------------------------------
pub trait HasIndexType {
type Index: Idx;
}
impl<T> HasIndexType for [T] {
type Index = usize;
}
impl<T> HasIndexType for Vec<T> {
type Index = usize;
}
impl<I: Idx, V: ?Sized> HasIndexType for Indexed<I, V> {
type Index = I;
}
impl<'a, V: ?Sized + HasIndexType> HasIndexType for &'a V {
type Index = V::Index;
}
impl<'a, V: ?Sized + HasIndexType> HasIndexType for &'a mut V {
type Index = V::Index;
}
impl<V: ?Sized + HasIndexType> HasIndexType for Box<V> {
type Index = V::Index;
}
impl<A, B, I: Idx> HasIndexType for (A, B)
where
A: HasIndexType<Index=I>,
B: HasIndexType<Index=I>,
{
type Index = I;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment