Last active
July 30, 2018 01:24
-
-
Save ExpHP/811e7a650754dc87ebe5d263cf8bef4d to your computer and use it in GitHub Desktop.
crazy index polymorphism
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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