#type #witness #type-id #equality #dispatch #no-alloc

no-std cisness

两种类型相同的“实时见证”运行时

3 个稳定版本

1.1.0 2024 年 7 月 16 日
1.0.1 2024 年 7 月 13 日

#591Rust 模式

Download history 298/week @ 2024-07-12 28/week @ 2024-07-19 39/week @ 2024-07-26 7/week @ 2024-08-02

372 每月下载量
用于 tisel

MPL-2.0 许可证

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