Created
June 5, 2018 19:10
-
-
Save ExpHP/113ce30117aa72c485f493514ddef66a to your computer and use it in GitHub Desktop.
index_cast.rs
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
/* ************************************************************************ ** | |
** This file is licensed under EITHER the MIT license or the Apache 2.0 ** | |
** license, at your option. ** | |
** ** | |
** http://www.apache.org/licenses/LICENSE-2.0 ** | |
** http://opensource.org/licenses/MIT ** | |
** ************************************************************************ */ | |
use ::Idx; | |
use ::std::marker::PhantomData; | |
use ::std::mem; | |
enum Void {} | |
pub struct KeepIt(Void); | |
pub struct CastIt<V: ?Sized>(PhantomData<V>, Void); | |
/// Trait for `index_cast`. See the free function for details. | |
/// | |
/// # Safety | |
/// | |
/// This is unsafe because it has generic impls that perform transmute. | |
pub unsafe trait IndexCast<Result: ?Sized, Disambig: ?Sized> { | |
fn index_cast(self) -> Result | |
where Self: Sized, Result: Sized, | |
{ unsafe { mem::transmute_copy(&mem::ManuallyDrop::new(self)) } } | |
} | |
/// Takes a single index type embedded somewhere in a larger type, and | |
/// casts it into another index type. (e.g. `&[Vec<(usize, f64)>]` | |
/// to `&[Vec<(MyIdx, f64)>]`). | |
/// | |
/// This uses overlapping impls with an extra type parameter to serve | |
/// as a disambiguator (the same technique used to implement HLists in | |
/// Rust). As long as you annotate the output type, and there is only | |
/// one index type that changes from the input to the output, type | |
/// inference will compute the disambiguator. | |
pub fn index_cast<V, Result, Disambig: ?Sized>(value: V) -> Result | |
where V: IndexCast<Result, Disambig>, | |
{ IndexCast::index_cast(value) } | |
// !!!!!!!!!!!!!!! | |
// So... basically there's no way to make this work for a large variety of | |
// types without writing implementations for each one. | |
// | |
// Let's just be pragmatic here. If you use cast_index on a type and it doesn't | |
// work, add an impl. Otherwise, who cares? | |
// !!!!!!!!!!!!!!! | |
// base case for a successful cast | |
unsafe impl<Src: Idx, Dest: Idx> IndexCast<Dest, KeepIt> for Src {} | |
// mods are just for organization | |
// (and, maybe you have an IDE that can collapse them) | |
mod basic { | |
use super::*; | |
unsafe impl<A1, B1, Dis> IndexCast<(B1, ), (Dis, )> for (A1, ) | |
where A1: IndexCast<B1, Dis> {} | |
unsafe impl<A1, A2, B, Dis> IndexCast<(B, A2), (CastIt<Dis>, KeepIt)> for (A1, A2) | |
where A1: IndexCast<B, Dis> {} | |
unsafe impl<A1, A2, B, Dis> IndexCast<(A1, B), (KeepIt, CastIt<Dis>)> for (A1, A2) | |
where A2: IndexCast<B, Dis> {} | |
unsafe impl<A1, A2, A3, B, Dis> IndexCast<(B, A2, A3), (CastIt<Dis>, KeepIt, KeepIt)> for (A1, A2, A3) | |
where A1: IndexCast<B, Dis> {} | |
unsafe impl<A1, A2, A3, B, Dis> IndexCast<(A1, B, A3), (KeepIt, CastIt<Dis>, KeepIt)> for (A1, A2, A3) | |
where A2: IndexCast<B, Dis> {} | |
unsafe impl<A1, A2, A3, B, Dis> IndexCast<(A1, A2, B), (KeepIt, KeepIt, CastIt<Dis>)> for (A1, A2, A3) | |
where A3: IndexCast<B, Dis> {} | |
unsafe impl<'a, A: ? Sized, B: ? Sized, Dis: ? Sized> IndexCast<&'a B, &'a Dis> for &'a A | |
where A: IndexCast<B, Dis> {} | |
unsafe impl<'a, A: ? Sized, B: ? Sized, Dis: ? Sized> IndexCast<&'a mut B, &'a mut Dis> for &'a mut A | |
where A: IndexCast<B, Dis> {} | |
unsafe impl<A, B, Dis> IndexCast<[B], [Dis]> for [A] | |
where A: IndexCast<B, Dis> {} | |
unsafe impl<A, B, Dis> IndexCast<Vec<B>, Vec<Dis>> for Vec<A> | |
where A: IndexCast<B, Dis> {} | |
} | |
mod collections { | |
use super::*; | |
use ::std::collections::{HashMap, BTreeMap}; | |
use ::std::collections::{HashSet, BTreeSet}; | |
unsafe impl<A, B, V, Dis> IndexCast<BTreeMap<B, V>, BTreeMap<CastIt<Dis>, KeepIt>> for BTreeMap<A, V> | |
where A: IndexCast<B, Dis> {} | |
unsafe impl<A, B, K, Dis> IndexCast<BTreeMap<K, B>, BTreeMap<KeepIt, CastIt<Dis>>> for BTreeMap<K, A> | |
where A: IndexCast<B, Dis> {} | |
unsafe impl<A, B, V, Dis> IndexCast<HashMap<B, V>, HashMap<CastIt<Dis>, KeepIt>> for HashMap<A, V> | |
where A: IndexCast<B, Dis> {} | |
unsafe impl<A, B, K, Dis> IndexCast<HashMap<K, B>, HashMap<KeepIt, CastIt<Dis>>> for HashMap<K, A> | |
where A: IndexCast<B, Dis> {} | |
unsafe impl<A, B, Dis> IndexCast<HashSet<B>, HashSet<Dis>> for HashSet<A> | |
where A: IndexCast<B, Dis> {} | |
unsafe impl<A, B, Dis> IndexCast<BTreeSet<B>, BTreeSet<Dis>> for BTreeSet<A> | |
where A: IndexCast<B, Dis> {} | |
} | |
#[test] | |
fn test_it() { | |
newtype_index!(A); | |
newtype_index!(B); | |
newtype_index!(C); | |
let x: &usize = &0; | |
let _: &A = index_cast(x); | |
// explicit disambiguator. All instances of KeepIt can be inferred. | |
let x: &[(f64, Vec<(B, A, f64)>)] = &[]; | |
let _: &[(f64, Vec<(B, C, f64)>)] = index_cast::<_, _, &[(_, CastIt<Vec<(_, CastIt<_>, _)>>)]>(x); | |
// actually, that disambiguator was not necessary. | |
let x: &[(f64, Vec<(B, A, f64)>)] = &[]; | |
let _: &[(f64, Vec<(B, C, f64)>)] = index_cast(x); | |
} | |
#[allow(unused)] // compiletest | |
fn holy_shnozballs_it_even_works_in_generic_contexts<I: Idx, J: Idx>() | |
{ | |
use ::std::collections::BTreeMap; | |
newtype_index!(B); | |
let x: &[(f64, Vec<(B, BTreeMap<usize, I>, f64)>)] = &[]; | |
let _: &[(f64, Vec<(B, BTreeMap<usize, J>, f64)>)] = index_cast(x); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment