# `cisness` Crate containing a type to "live-witness" that two types are the same (["cismutable"][cismute]). In particular, it lets you assert that a codepath will not actually be executed at runtime if the types are NOT the same (and panic if it does), which is useful for - for instance - checking types via [`TypeId`][typeid], and selecting operations with type-specific output where you want the type specificity can be transferred to the user. The key structure you want to use this library, is [`LiveWitness`][live-witness]. Using it "bare" from this library is somewhat clunky, but it can be used to build better abstractions (the author is working on a small crate called `tisel` that isn't published yet that will use this as a basis). ## Example Here's an example of how to use [`LiveWitness`][live-witness] to invoke a trait method that has an associated type that can be output. Of course, in such a simple case, this isn't very useful, but it is much more useful in other situations. ```rust use cisness::LiveWitness; use std::{collections::HashMap, any::{TypeId, Any}}; pub trait PartyMember { type Age: core::fmt::Display; fn get_age(&self) -> Self::Age; } #[derive(Debug, Clone)] pub struct Human { pub name: String, pub age: u32, } impl PartyMember for Human { type Age = u32; fn get_age(&self) -> Self::Age { self.age } } #[derive(Debug, Clone)] pub struct Dragon { pub name: String } impl PartyMember for Dragon { type Age = &'static str; fn get_age(&self) -> Self::Age { "Unknowable and Eldritch" } } /// Party that stores the vocabulary "member" types in one hashmap dynamically. /// Note that clearly, in this case, there are better ways to do it - but often that would not be the case. #[derive(Default)] pub struct AdventuringParty { // Maps the `Member` type id to a vector of members of that type. members: HashMap> } impl AdventuringParty { pub fn add_member(&mut self, member: M) { let new_member_typeid = TypeId::of::(); self.members .entry(new_member_typeid) .or_insert_with(|| Box::new(Vec::::new())) .downcast_mut::>() .expect(" is mapped to Vec and we just inited") .push(member); } // Note here - the better way to do this specific example would be to put a trait criteria on // the `M` generic requiring `Ord`, and doing downcasting. However, for illustration purposes, we'll // be listing out supported types and manually implementing them, returning `None` for unorderable ages. pub fn get_oldest_age_of_type(&self) -> Option<::Age> { // Note that you could do this with `Any::downcast_ref`. However, that doesn't work if you can't // get a value of `M`, and it doesn't work as well for more complex cases. // To illustrate, we're going to use TypeId matching, even if for this case it would work better not to do that. let type_id = TypeId::of::(); match type_id { t if t == TypeId::of::() => { // Note here that we don't have to directly witness the type of the generic parameter, we can instead witness // the output (if we know it), or a vector. // In this case, we're asserting that we'll only run through this code if the output type is the same as // `Option` - this lets us do "magical" coercions that will panic if we've made an error. let w = LiveWitness::::Age>, Option>::only_executed_if_same(); let output = self.members.get(&t) .and_then(|v| v.downcast_ref::>()) .map(|v| v.iter().map(PartyMember::get_age).max()).flatten(); // Note here - our witness lets us turn this to the generic output, and will typecheck even if `M` != `Human` - because we don't go down // this path, it's fine. w.owned(output) }, t if t == TypeId::of::() => { let w = LiveWitness::::Age>, Option>::only_executed_if_same(); if !self.members.get(&t).is_some() { return None }; // Note how at no point here did we actually have to downcast any values. let output = Some("[ELDRICH MEMETIC HAZARD]"); w.owned(output) } _ => None } } } ``` ## The Name Yes, it's intentional. The author is trans 🏳️‍⚧️, and finds it funny. [live-witness]: https://docs.rs/cisness/latest/cisness/struct.LiveWitness.html [cismute]: https://docs.rs/cismute [typeid]: https://doc.rust-lang.org/nightly/core/any/struct.TypeId.html