我最近试图实现一个系统,该系统允许每 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

这些错误令(我)困惑,我不确定如何解决它们.是否有可能以处理程序参数可以从Stateborrow 的方式编写API,或者它们必须是Cloned?

删除此行将导致编译错误消失,但这意味着不会向Executor添加Handler,并且不会打印该消息.

推荐答案

不幸的是,这是做不到的.

我不太确定第一个错误说的是什么,但第二个错误说ErasedHandle应该包含一个函数,它可以在any个生命周期(for<'lifetime> &'lifetime HashMap)中使用&'lifetime HashMap,但实际上您给了它一个闭包,对于add_handler()中定义的特定生命周期'a,它只需要&'a HashMap个生命周期.

另一种解读方式是:'a是什么?我们调用run()内部的处理程序,生存期为&self,但这是&self of 101的生存期,当我们调用之前的add_handler()时,它不存在.

我们需要一个排名更高的生命周期,但不幸的是,我们不能具体说明它,我将简要地解释为什么(因为长时间的解释将是...Long).go 掉'a并只指定T: for<'a> Param<'a>是很诱人的,但如果我们指定H: for<'a> Handler<'a, T>,或者如果我们只是go 掉Handler中的'a,我们就遇到了一个不可逾越的障碍:我们不能在类型系统中捕获我们想要的具有HRTB生存期的闭包,也就是说,使用for<'a> &'a Foo,我们所能做的就是对于某个特定的生存期'a,我们所能做的就是&'a Foo.

Rust相关问答推荐

无需通过ASIO输入音频,并使用cpal进行反馈示例

如何处理动态 struct 实例化?

在泛型 struct 的字段声明中访问关联的Conant

下载压缩文件

使用Clap时如何将String作为Into Str参数传递?

如何实现泛型枚举的`Serde::Desialize`特性

使用极点数据帧时,找不到枚举结果的方法lazy()

为什么基于高山Linux的Docker镜像不能在绝对路径下找到要执行的命令?

我应该将哪些文件放入我的GitHub存储库

获取与父字符串相关的&;str的原始片段

在macro_rule中拆分模块和函数名

如何在Rust中缩短数组

相当于 Rust 中 C++ 的 std::istringstream

有什么办法可以追踪泛型的单态化过程吗?

为什么我不能克隆可克隆构造函数的Vec?

字符串切片的向量超出范围但原始字符串仍然存在,为什么判断器说有错误?

为什么可以从闭包中返回私有 struct

`if let` 只是另一种编写其他 `if` 语句的方式吗?

list 中没有指定目标 - 必须存在 src/lib.rs、src/main.rs、[lib] 部分或 [[bin]] 部分

为什么 Bevy 的 Trait 边界不满足 Rapier 物理插件?