rust 如何按名称设置可用工厂的全局表?

7cwmlq89  于 2023-01-21  发布在  其他
关注(0)|答案(1)|浏览(114)

我试图建立一个可用的工厂列表,以建立不同种类的处理器在 rust 。

pub trait MyHandler {
    fn handle();
}

pub trait MyFactory {
    fn build() -> Result<Box<dyn MyHandler>, dyn Error>;
}

我有一些具体的处理程序(Foo,和类似的酒吧和巴兹...):

pub struct MyFoo;

impl MyHandler for MyFoo {
    fn handle() {
        println!("Foo handler");
    }
}

impl MyFactory for MyFoo {
    fn build() -> Result<Box<dyn MyHandler>, dyn Error> {
        // Do something that may fail
        Ok(MyFoo {})
    }
}

现在,我想构建一个“按名称列出的可用处理程序的全局表”。
到目前为止,我试过:

  • const NOTIFIERS: HashMap<(&str, dyn Fn() -> Result<Box<dyn MyHandler>, Box<dyn Error>)>;不起作用,因为我必须.insert()数据,但常量不能修改。
  • 不编译的const NOTIFIERS: Vec<(&str, Box<dyn Fn() -> Result<Box<dyn MyHandler>, Box<dyn Error>>>)> = vec![ ("foo", Box::new(MyFoo::build)), ("bar", Box::new(MyBar::build)), ("baz", Box::new(MyBaz::build))];,因为常量中不允许分配

我的目标是这种结构,这样我就可以:

  • 在添加/删除处理程序时,具有要更新的单一事实源
  • 我有一个名称列表,可以轻松地在API或CLI中迭代和提供
  • 我有一个简单的全局处理程序工厂,可以在其中查找处理程序名称并调用构建

除了上述两种尝试之外,

  • 我想使用一个枚举,其中每个类型都是(&str, closure)的元组,我可以使用strum板条箱对其进行迭代,但它会在枚举变体(类型)上进行迭代,我仍然必须从其他地方设置枚举值(名称和闭包)...
  • 我想我可能会使用宏来自动填充处理程序列表,方法是“简单地”向每种类型的处理程序添加一个#[derive(RegisterHandler),但我对Rust太陌生了,不了解关于宏的任何东西,即使它是在构建时生成的,我仍然会有同样的“问题”,即如何将结果结构“存储”为特定类型的常量。

现在,我手动硬编码处理程序名称两次:一次是提供处理程序类型列表,第二次是匹配请求的处理程序名以调用其工厂。它可以工作,但看起来不优雅。
我可以按名称构建“全球工厂”吗?如何“正确”地构建?

ruarlubt

ruarlubt1#

这绝对有可能,你只需要做一些调整。

使用静态,而不是常量。

一个const变量在使用站点被“复制”。这通常不是你想要的。相反,你可以使用一个static变量--它在程序中只有一个示例。一个static变量仍然需要一个const初始化器。

保持简单。

你不需要MyFactory trait,Rust很好地理解了函数指针:

type Factory = fn() -> Result<Box<dyn MyHandler>, Box<dyn Error>>;
  • 次要注意事项:您需要装箱Error。*

把这些放在一起。

完整的with playground link示例:

use std::error::Error;

type Factory = fn() -> Result<Box<dyn MyHandler>, Box<dyn Error>>;

static FACTORIES: &[(&str, Factory)] = &[
    ("foo", create_foo),
    ("bar", create_bar),
    ("baz", create_baz),
];

pub trait MyHandler {
    fn handle(&self);
}

fn create_foo() -> Result<Box<dyn MyHandler>, Box<dyn Error>> { todo!() }
fn create_bar() -> Result<Box<dyn MyHandler>, Box<dyn Error>> { todo!() }
fn create_baz() -> Result<Box<dyn MyHandler>, Box<dyn Error>> { todo!() }

fn main() -> Result<(), Box<dyn Error>> {
    let factory = FACTORIES
        .iter()
        .filter_map(|t| (t.0 == "foo").then_some(t.1))
        .next()
        .unwrap();
    
    factory()?;
    
    Ok(())
}
  • 注意:这是一个概念验证,在生产中,我不建议公开FACTORIES。相反,我会将其私有化,并公开一个函数来执行查找,并在失败时提供一个漂亮的错误消息。*

相关问题