我最近试图实现一个系统,该系统允许每
rust-magic-function-params个魔术类型参数,并且可以从Context
对象borrow 参数.我构建了它的一个版本,增加了一个Executor
类型,它可以以一种通用的、擦除类型的方式调用Handler
:
通过链接,以下是该示例中最重要的部分:
trait Handler<'a, T: 'a>: 'static {
fn call(&self, value: T) -> Result<(), Error>;
}
impl<'a, T: 'a, F> Handler<'a, T> for F
where
F: Fn(T) -> Result<(), Error> + 'static,
{
fn call(&self, value: T) -> Result<(), Error> {
(self)(value)
}
}
Handler
特征负责调用该函数.它还有一个新的生命周期 参数'a
,call()
中的value
参数必须为它而活.
trait Param<'a>: Sized + 'a {
fn from_state(state: &'a HashMap<TypeId, Box<dyn Any>>) -> Result<Self, Error>;
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
struct StateVar<'a, T: ?Sized>(&'a T);
impl<'a, T: Any> Param<'a> for StateVar<'a, T> {
fn from_state(state: &'a HashMap<TypeId, Box<dyn Any>>) -> Result<Self, Error> {
state
.get(&TypeId::of::<T>())
.and_then(|any| any.downcast_ref::<T>().map(StateVar))
.ok_or_else(|| Error::from("Parameter not present"))
}
}
Param<'a>
特性用于从state
创建变量的实例,然后可以将该实例传递给Handler
‘S参数(类似于上面链接的axum
示例中的FromContext
特性).StateVar<'a, T>
实现了从state
获取引用的Param<'a>
特征.
struct ErasedHandler(Box<dyn Fn(&HashMap<TypeId, Box<dyn Any>>) -> Result<(), Error>>);
#[derive(Default)]
struct Executor {
handlers: Vec<ErasedHandler>,
state: HashMap<TypeId, Box<dyn Any>>,
}
impl Executor {
#[allow(unused)]
fn add_handler<'a, H, T>(&mut self, handler: H)
where
T: Param<'a> + 'a,
H: Handler<'a, T>,
{
// ...
}
fn add_var<T: Any>(&mut self, value: T) {
self.state.insert(TypeId::of::<T>(), Box::new(value));
}
fn run(&self) -> Result<(), Error> {
for handler in &self.handlers[..] {
(handler.0)(&self.state)?;
}
Ok(())
}
}
ErasedHandler
包含包装在盒装的lambda中的类型擦除处理程序.Executor
struct 保存所有注册的处理程序以及状态.add_var()
方法添加StateVar
,run()
方法运行使用所包含的state
的所有处理程序.
Executor::add_handler
的实施情况是:
impl Executor {
fn add_handler<'a, H, T>(&mut self, handler: H)
where
T: Param<'a> + 'a,
H: Handler<'a, T>,
{
fn make_wrapper<'a_, H_, T_>(
handler: H_,
) -> Box<dyn Fn(&'a_ HashMap<TypeId, Box<dyn Any>>) -> Result<(), Error>>
where
T_: Param<'a_> + 'a_,
H_: Handler<'a_, T_>,
{
Box::new(move |state: &'a_ HashMap<TypeId, Box<dyn Any>>| {
T_::from_state(state).and_then(|param| handler.call(param))
})
}
let wrapper = make_wrapper::<'a, H, T>(handler);
self.handlers.push(ErasedHandler(wrapper));
}
}
其中它将handler
移动到盒装包装器lambda中,然后将其添加到其handlers
Vec
中.
然后可以调用该系统,如main
函数所示:
fn main() -> Result<(), Error> {
fn my_handler(StateVar(value): StateVar<'_, i32>) -> Result<(), Error> {
println!("Stored state value = {value}");
Ok(())
}
let mut executor = Executor::default();
executor.add_var::<i32>(42);
executor.add_handler(my_handler);
executor.run()?;
Ok(())
}
其中可以添加任意数量的变量(唯一类型)和handler
来顺序运行.运行此main
函数时,理想情况下应打印:
Stored state value = 42
但是,try 运行此程序会导致编译错误:
error: lifetime may not live long enough
--> src/main.rs:67:42
|
49 | fn add_handler<'a, H, T>(&mut self, handler: H)
| -- lifetime `'a` defined here
...
67 | self.handlers.push(ErasedHandler(wrapper));
| ^^^^^^^ cast requires that `'a` must outlive `'static`
error[E0308]: mismatched types
--> src/main.rs:67:42
|
67 | self.handlers.push(ErasedHandler(wrapper));
| ^^^^^^^ one type is more general than the other
|
= note: expected trait object `dyn for<'a> Fn(&'a HashMap<TypeId, Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error + Send + Sync>>`
found trait object `dyn Fn(&HashMap<TypeId, Box<dyn Any>>) -> Result<(), Box<dyn std::error::Error + Send + Sync>>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 2 previous errors
这些错误令(我)困惑,我不确定如何解决它们.是否有可能以处理程序参数可以从State
borrow 的方式编写API,或者它们必须是Clone
d?
删除此行将导致编译错误消失,但这意味着不会向Executor
添加Handler
,并且不会打印该消息.