我有一个Rust项目,其中有以下结构:
|- src
| |- main.rs
| |- handler.rs
| |- modules
| | |- handler1.rs
| | |- handler2.rs
| | | ...
使用如下伪代码:
//handler.rs
trait Handler {
fn handle(message: str) -> bool;
}
// handler1.rs
struct Handler1;
impl Handler for Handler1 {
fn handle(message: str) -> bool {
//...
}
}
//handler2.rs
struct Handler2;
impl Handler for Handler2 {
fn handle(message: str) -> bool {
//...
}
}
//main.rs
const handlers: Vec<Handler> = vec![Box::new(Handler1 {}), Box::new(Handler2 {})];
fn handleMessage(myMessage: str) {
for handler in handlers.iter() {
if (handler.handle(myMessage)) {
break;
};
}
}
我想从项目的结构中自动创建这个handlers
常量。我认为有两种方法可以奏效:
1.在每个模块中使用宏,类似于(?):
//handler2.rs
struct Handler2;
impl Handler for Handler2 {
fn handle(message: str) -> bool {
//...
}
}
registerHandler!{Handler2}
1.也许有一种方法可以在构建时扫描所有模块中的Handler
trait实现?(或者扫描一些属性标记的实现?)
我也不知道该怎么做,有没有一个标准的方法来解决这类问题?
如果这是显而易见的,新的生 rust ,所以道歉的任何大错误!我怀疑这是不平凡的,但任何关于问题/标准方法的资源都将非常有帮助。
2条答案
按热度按时间n8ghc7c11#
到目前为止,最好的解决方案与最低的维护要求,我最终解决了如下:
handler.rs
包含Handler
trait:handlers.rs
在构建时通过build.rs
生成:cargo.toml
必须提供构建条目:最后
build.rs
看起来像这样:上面的代码假设你的模块与结构体/处理程序本身的名字相同(只是大小写不同)。A full example is on github。
编辑:我重构了构建脚本,现在可能可读性更好了:
nlejzf6q2#
这是一种很容易通过虚拟调用解决的情况。
你只需要添加
dyn
来创建一个可以进行虚拟调用的box/reference。请注意,您不需要存放盒子。您可能需要存储引用:
如果你想避免虚调用,有一个递归数据结构的选项。此选项可能提供最佳运行时性能(因为它允许编译器内联处理程序实现并展开循环),但可能导致更长的编译时间。
如果你的处理程序比较慢或者这个代码的性能不是很关键,我会建议使用动态分派,否则
HandlerSet
方法会更好。