7 个版本
新 0.2.5 | 2024年7月31日 |
---|---|
0.2.4 | 2023年6月29日 |
0.1.0 | 2023年6月21日 |
#367 在 Rust 模式
184 每月下载
14KB
61 行
确定映射
确保项存在的一种类型化映射。
解决的问题
Rust 中,服务抽象通常用于模块化结构设计,例如 tower-service 或 service-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