如何根据名称建立可用工厂的全局表?



我正在尝试构建一个可用的工厂列表,以在Rust中构建不同类型的处理程序。

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

我有几个具体的处理程序(Foo,和类似的Bar和Baz…):

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不能修改。

  • 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)的元组,我可以使用strumcrate进行迭代,但它会迭代枚举变体(类型),我仍然需要从其他地方设置枚举值(名称和闭包)…

  • 我想我可能会使用宏来自动填充处理程序列表,通过"简单地"添加一个#[derive(RegisterHandler)到每种类型的处理程序,但我对Rust太陌生了,无法理解关于宏的任何事情,即使它是在构建时生成的,我仍然会有相同的"问题"关于"存储"结果结构作为特定类型的const。

现在,我手动硬编码处理程序名称两次:一次提供处理程序类型列表,第二次匹配请求的处理程序名称以调用其工厂。它可以工作,但看起来不优雅。

我可以冠名建造全球工厂吗?以及如何"正确地"做到这一点。

?

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

使用static,而不是const。

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

保持简单。

你不需要MyFactorytrait, Rust理解函数指针很好:

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

小提示:您需要框Error

把它们放在一起。

游乐场链接的完整示例:

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。相反,我会将其设置为模块私有,并公开一个函数来进行查找,并在失败时提供一个很好的错误消息。

相关内容

  • 没有找到相关文章

最新更新