事件处理,无需包含所有事件的巨大枚举

问题描述 投票:0回答:1

很长一段时间以来,我的目标都是实现这样的 API:

struct GetEvent;

fn handler(_event: &mut GetEvent) {}

pub fn main() {
    let mut server = Server::default();

    server.handle(handler);

    server.emit(PostEvent);
}

名称和类型只是示例,这个实际程序与 HTTP 无关

这两天,我又回到了这个问题。有几次我感觉如此接近,但我就是不太明白。我真的是拔头发了

下面你会发现我尝试过的事情,如果你立即知道该怎么做并且比我更聪明,你可以跳过所有这些:D

首先,基本结构可以在这里获得。这个可以编译,因此相关方法已被

todo!()
打断。

所以,我们开始吧:

1.就装箱吧!

天真地,我只是查找如何使用any,并创建了这个哈希映射:

struct Server {
    handlers: HashMap<TypeId, Box<dyn Handler<dyn Any>>>,
}

fn handle<E>(&mut self, handler: impl Handler<E>) {
    self.handlers.insert(TypeId::of::<E>(), Box::new(handler));
}

但是:

 --> src/main.rs:26:49
   |
26 |         self.handlers.insert(TypeId::of::<E>(), Box::new(handler));
   |                                                 ^^^^^^^^^^^^^^^^^ the trait `for<'a> Fn(&'a mut (dyn Any + 'static))` is not implemented for `impl Handler<E>`, which is required by `impl Handler<E>: Handler<(dyn Any + 'static)>`
   |

老实说我不知道这意味着什么。但我假设,这行不通。

游乐场

2.任何一路通过!

struct Server {
    handlers: HashMap<TypeId, Box<dyn Any>>,
}

fn handle<E: 'static>(&mut self, handler: impl Handler<E>) {
    self.handlers.insert(TypeId::of::<E>(), Box::new(handler));
}

这...可以编译!胜利!遗憾的是没有。在阅读了一个小时有关向下转换的内容后,我遇到了运行时错误!

fn emit<E: 'static>(&mut self, mut event: E) {
    let Some(handler) = self.handlers.get_mut(&TypeId::of::<E>()) else {
        println!("did not find handler...");
        return;
    };

    let handler = handler.downcast_ref::<Box<dyn Handler<E>>>().unwrap();

    handler.handle(&mut (event as E));
}
thread 'main' panicked at src/main.rs:36:69:
called `Option::unwrap()` on a `None` value

但我想我明白为什么这不起作用。注册函数

fn handle<E: 'static>(...)
将转为静态调度,因此类型不会是
Box<dyn Handler<E>>
,而是:
alloc::boxed::Box<rust_test::handler>
。这是我的处理程序的静态函数指针。

游乐场

3.那么首先将其转换为
Handler
特质吗?

fn handle<E: 'static>(&mut self, handler: impl Handler<E>) {
    let handler = Box::new(handler) as Box<dyn Handler<E>>;
    self.handlers.insert(TypeId::of::<E>(), handler);
}

不!因为...呃,不!

     = note: expected struct `Box<(dyn Any + 'static)>`
                found struct `Box<dyn Handler<E>>`

啊哈!需要

Any + 'static
作为处理程序的基本特征?显然不是:

cannot cast `dyn Handler<E>` to `dyn Any`, trait upcasting coercion is experimental

游乐场

4.寻求非人类的帮助🤖*

*人工智能没有直接生成任何代码或任何其他内容。一切都已经转变并经过我的审查。

“不可能...某种类型的擦除...bla bla”。

好的,当然:

trait HandlerErased {
    fn handle(&self, event: &mut dyn Any);
}

impl<E, H: Handler<E>> HandlerErased for H {
    fn handle(&self, event: &mut dyn Any) {
        // Check if correct type
        todo!();
    }
}

虽然我不太喜欢这段代码,但在此处检查类型是否正确似乎有点错误。此时,无论如何它应该是正确的处理程序类型。但我已经绝望了,所以发送吧!

一如既往......不起作用,只会在面对时产生更多幻觉:

error[E0207]: the type parameter `E` is not constrained by the impl trait, self type, or predicates
  --> src/main.rs:24:6
   |
24 | impl<E, H: Handler<E>> HandlerErased for H {
   |      ^ unconstrained type parameter

游乐场


我尝试了一些其他的事情,但我认为其中没有一个值得注意。是的,我知道我可以使用一个巨大的 Enum,但这就是我现在正在做的事情,而且 API 相当混乱。这样做的目标是,API 的用户可以选择他们想要处理的事件。

此外,这可能允许我检测(无需成为宏向导2)并根据它生成一些值(服务器功能,如果您感兴趣的话)。

我已阅读这些资源以及更多内容:

  1. https://users.rust-lang.org/t/event-handlers-with-generic-arguments-in-a-hashmap-is-this-even-possible/44301 虽然这非常接近我想要的,但它直接引用处理程序中的
    Fn
    ,并且我希望它是基于特征的。我也无法理解这个
    Option
    生意是做什么的。
  2. https://github.com/alexpusch/rust-magic-patterns/tree/master 有趣,但据我所知并不真正相关。
  3. https://www.reddit.com/r/rust/comments/1dv2m5i/looking_for_advice_implementing_a_type_safe_event/ 更多想法集合。

PS:
- 这是关于什么的...
-我恳请编辑战士不要删除上下文,和/或消除这个问题。谢谢!

rust
1个回答
0
投票

这可以完成,但需要一些装箱层 - 您需要一层来统一不同的

Handler<E>
实现 (
Box<dyn Handler<E>>
),并且需要另一层来统一这些框以擦除
E
后面的
Any
类型(
Box<dyn Any>
)。

首先,服务器定义如下:

#[derive(Default)]
struct Server {
    handlers: HashMap<TypeId, Box<dyn Any>>,
}

添加处理程序需要对其进行装箱。我们会让来电者处理这个问题。

fn handle<E: 'static>(&mut self, handler: Box<dyn Handler<E>>) {
    let boxed: Box<dyn Any> = Box::new(handler);
    self.handlers.insert(TypeId::of::<E>(), boxed);
}

请注意,我们必须绑定

E: 'static
,因为
TypeId
不会对生命周期进行编码,因为生命周期的可能数量在技术上是无限的。

要使用它,我们必须将

Any
向下转换回
Box<dyn Handler<E>>
。 这里我只是
unwrap()
结果,因为错误是意外的并且表明操作
handlers
映射的函数中存在错误,因此恐慌是适当的。

fn emit<E: 'static>(&mut self, mut event: E) {
    if let Some(boxed) = self.handlers.get(&TypeId::of::<E>()) {
        boxed
            .downcast_ref::<Box<dyn Handler<E>>>()
            .unwrap()
            .handle(&mut event);
    }
}

注意,我们可以使用一些

unsafe
技巧来避免双重装箱,但只有当性能实际上是这种方法的问题时,我才会使用这些技巧。

© www.soinside.com 2019 - 2024. All rights reserved.