3 个稳定版本
1.1.0 | 2024 年 7 月 16 日 |
---|---|
1.0.1 | 2024 年 7 月 13 日 |
#591 在 Rust 模式
372 每月下载量
用于 tisel
15KB
123 行
cisness
包含一个类型以“实时见证”两种类型相同的 crate ("cismutable")。
特别是,它允许您断言如果类型不相同,代码路径实际上在运行时不会执行(如果执行则 panic),这在通过 TypeId
检查类型或选择具有特定输出操作时非常有用,其中您希望将类型特异性传递给用户。
您想使用此库的关键结构是 LiveWitness
。直接从此库使用它可能有些笨拙,但它可以用来构建更好的抽象(作者正在开发一个名为 tisel
的未发布的小 crate,它将以此为基础)。
示例
以下是如何使用 LiveWitness
来调用具有关联类型的 trait 方法,该关联类型可以输出。当然,在这种情况下,这并不很有用,但在其他情况下非常有用。
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<TypeId, Box<dyn Any>>
}
impl AdventuringParty {
pub fn add_member<M: Any>(&mut self, member: M) {
let new_member_typeid = TypeId::of::<M>();
self.members
.entry(new_member_typeid)
.or_insert_with(|| Box::new(Vec::<M>::new()))
.downcast_mut::<Vec::<M>>()
.expect("<typeid> is mapped to Vec<type for typeid> 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<M: Any + PartyMember>(&self) -> Option<<M as PartyMember>::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::<M>();
match type_id {
t if t == TypeId::of::<Human>() => {
// 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<Human::Age>` - this lets us do "magical" coercions that will panic if we've made an error.
let w = LiveWitness::<Option<<Human as PartyMember>::Age>, Option<M::Age>>::only_executed_if_same();
let output = self.members.get(&t)
.and_then(|v| v.downcast_ref::<Vec<Human>>())
.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::<Dragon>() => {
let w = LiveWitness::<Option<<Dragon as PartyMember>::Age>, Option<M::Age>>::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
}
}
}
名称
是的,这是故意的。作者是跨性别者,觉得这很有趣。
依赖项
~18KB