7 个版本

0.2.5 2024年7月31日
0.2.4 2023年6月29日
0.1.0 2023年6月21日

#367Rust 模式

Download history 2/week @ 2024-04-14 24/week @ 2024-04-28 45/week @ 2024-05-19 42/week @ 2024-05-26 35/week @ 2024-06-02 4/week @ 2024-06-16 45/week @ 2024-06-23 7/week @ 2024-06-30 9/week @ 2024-07-07 27/week @ 2024-07-14 148/week @ 2024-07-28

184 每月下载

MIT/Apache

14KB
61

确定映射

Crates.io

确保项存在的一种类型化映射。

解决的问题

Rust 中,服务抽象通常用于模块化结构设计,例如 tower-serviceservice-async。服务分层,请求/响应类型可能在不同的层中变化。当不同层之间的组件有数据依赖关系时,尤其是间接的依赖关系时,通过修改请求/响应类型传递所有所需信息变得具有挑战性。如果要传递的变量数量波动,我们必须重新定义一个结构来适应这些变化。这需要为这些结构实现转换函数和数据提取函数,这可能是繁琐的,并且会弄乱代码。通常,我们通过使用 HashMap 或 TypeMap 来管理需要在服务之间传递的信息来避免这种情况。

然而,这种方法有一个显著的缺点:在读取时,我们无法在编译时确保后续组件所需的键值对已被设置。这可能导致程序中不必要的错误处理分支或在某些场景中崩溃。这个包在键插入或删除时转换结构类型,确保某些值在编译时存在。

如果您需要使用结构在多个阶段之间传递信息,这个包非常适合您。

它遵守承诺:如果它编译,它就会工作。

内部工作原理

pub type EmptyContext = Context<::certain_map::Vacancy, ::certain_map::Vacancy>;
pub type FullContext =
    Context<::certain_map::Occupied<PeerAddr>, ::certain_map::Occupied<Option<RemoteAddr>>>;
#[derive(Debug, Clone)]
pub struct Context<_CMT_0, _CMT_1> {
    peer_addr: _CMT_0,
    remote_addr: _CMT_1,
}

// `ParamSet for PeerAddr will not compile if it has 
// been previously set.
impl<_CMT_0, _CMT_1> ::certain_map::ParamSet<PeerAddr> for Context<_CMT_0, _CMT_1> {
    type Transformed = Context<::certain_map::Occupied<PeerAddr>, _CMT_1>;
    #[inline]
    fn param_set(self, item: PeerAddr) -> Self::Transformed {
        Context {
            peer_addr: ::certain_map::Occupied(item),
            remote_addr: self.remote_addr,
        }
    }
}

// `ParamRef<PeerAddr>` trait bound will not compile for maps
// where it hasn't been set with `ParamSet<PeerAdr>.
impl<_CMT_1> ::certain_map::ParamRef<PeerAddr>
    for Context<::certain_map::Occupied<PeerAddr>, _CMT_1>
{
    #[inline]
    fn param_ref(&self) -> &PeerAddr {
        &self.peer_addr.0
    }
}

使用方法

use certain_map::{certain_map, Param, ParamRef, ParamRemove, ParamSet, ParamTake};

struct UserName(String);

#[derive(Copy, Clone)]
struct UserAge(u8);

certain_map! {
    pub struct MyCertainMap {
        name: UserName,
        #[ensure(Clone)]
        age: UserAge,
    }
}

fn main() {
    let meta = MyCertainMap::new();

    // The following line fails to compile since there's no UserName in the map.
    // log_username(&meta);

    let meta = meta.param_set(UserName("ihciah".to_string()));
    // Now we can get it with certainty.
    log_username(&meta);

    let (meta, removed) = ParamTake::<UserName>::param_take(meta);
    assert_eq!(removed.0, "ihciah");
    // The following line fails to compile since the UserName is removed.
    // log_username(&meta);

    // We can also remove a type no matter if it exist.
    let meta = ParamRemove::<UserName>::param_remove(meta);

    let meta = meta.param_set(UserAge(24));
    // We can get ownership of fields with #[ensure(Clone)]
    log_age(&meta);
}

fn log_username<T: ParamRef<UserName>>(meta: &T) {
    println!("username: {}", meta.param_ref().0);
}

fn log_age<T: Param<UserAge>>(meta: &T) {
    println!("user age: {}", meta.param().0);
}

依赖关系

~280–730KB
~17K SLoC