代码之家  ›  专栏  ›  技术社区  ›  John Darkman

如何在流行的crates中完成具有未知数量参数的事件处理程序?

  •  0
  • John Darkman  · 技术社区  · 1 年前

    我越来越经常地注意到,不同的生锈箱使用用户想要的任何参数集来注册事件处理程序,而不是库开发人员。我在以下板条箱中看到过:axum、bevy engine、teloxide。

    我有一个问题,到底是如何在幕后完成的,库如何知道事件处理程序需要哪些参数,哪些参数可以有可变数量的参数,它们是如何传递给函数的?我想读一些关于这个话题的东西。我无法理解库如何在其他方法的参数中接受这样的处理程序方法,并且没有使用宏。

    1 回复  |  直到 1 年前
        1
  •  3
  •   Chayim Friedman    1 年前

    这究竟是如何工作的取决于图书馆的具体情况。但一般的模式是,用一个状态调用的方法来实现一个特性,然后在参数实现其他特性时,为每个最多有X个参数的函数实现它(使用宏),这意味着能够从状态中提取参数。

    例如:

    pub struct State {
        // ...
    }
    
    pub trait Extractor: Sized {
        fn extract(state: &mut State) -> Self;
    }
    
    pub trait Handler<Args> {
        fn call(&mut self, state: &mut State);
    }
    
    // The below is usually done with a macro.
    impl<F: FnMut()> Handler<()> for F {
        fn call(&mut self, _state: &mut State) {
            self()
        }
    }
    impl<Arg1: Extractor, F: FnMut(Arg1)> Handler<(Arg1,)> for F {
        fn call(&mut self, state: &mut State) {
            self(Arg1::extract(state))
        }
    }
    impl<Arg1: Extractor, Arg2: Extractor, F: FnMut(Arg1, Arg2)> Handler<(Arg1, Arg2)> for F {
        fn call(&mut self, state: &mut State) {
            self(Arg1::extract(state), Arg2::extract(state))
        }
    }
    
    pub fn call_handler<Args, H: Handler<Args>>(handler: &mut H, state: &mut State) {
        handler.call(state);
    }
    

    键入擦除处理程序(例如,为了将所有处理程序存储在一起)可以按如下方式完成:

    pub fn handler_to_dyn<Args, H: Handler<Args> + 'static>(
        mut handler: H,
    ) -> Box<dyn FnMut(&mut State)> {
        Box::new(move |state| handler.call(state))
    }
    

    尽管这通常伴随着额外的特征。

    推荐文章