问题陈述
我有一组结构体,A、B、C和D,它们都实现了一个特质Runnable。
我还有一个名为
在我的程序中,我想解析一个
这个可以工作,但是有一些代码味道。
有没有更优雅或习惯用语的方式来解决这些问题呢?
我有一组结构体,A、B、C和D,它们都实现了一个特质Runnable。
trait Runnable {
fn run(&mut self);
}
impl Runnable for A {...}
impl Runnable for B {...}
impl Runnable for C {...}
impl Runnable for D {...}
我还有一个名为
Config
的结构体,它用作构建A
、B
、C
和D
实例的规范。struct Config {
filename: String,
other_stuff: u8,
}
impl From<Config> for A {...}
impl From<Config> for B {...}
impl From<Config> for C {...}
impl From<Config> for D {...}
在我的程序中,我想解析一个
Config
实例,并根据filename
字段的值构造一个A
、B
、C
或D
,然后调用Runnable::run
。应该通过逐个检查每个结构体与filename
字符串进行匹配,并选择第一个与该字符串"匹配"的结构体。
天真的实现
这是一个天真的实现。
trait CheckFilename {
fn check_filename(filename: &str) -> bool;
}
impl CheckFilename for A {...}
impl CheckFilename for B {...}
impl CheckFilename for C {...}
impl CheckFilename for D {...}
fn main() {
let cfg: Config = get_config(); // Some abstract way of evaluating a Config at runtime.
let mut job: Box<dyn Runnable> = if A::check_filename(&cfg.filename) {
println!("Found matching filename for A");
Box::new(A::from(cfg))
} else if B::check_filename(&cfg.filename) {
println!("Found matching filename for B");
Box::new(B::from(cfg))
} else if C::check_filename(&cfg.filename) {
println!("Found matching filename for C");
Box::new(C::from(cfg))
} else if D::check_filename(&cfg.filename) {
println!("Found matching filename for D");
Box::new(D::from(cfg))
} else {
panic!("did not find matching pattern for filename {}", cfg.filename);
};
job.run();
}
这个可以工作,但是有一些代码味道。
- 巨大的
if else if else if else if else...
语句在我看来很臭 - 重复太多:用于检查文件名、打印所选结构类型以及根据配置构建实例的代码在每个分支中都是相同的,唯一的区别在于它们处理的结构类型不同。有没有办法将这种重复性抽象出来?
- 非常容易出错:很容易因为未能将结构与谓词同步而在文件名字符串和结构之间出现映射错误;例如,写出以下代码:
而编译器不会捕捉到这个错误。if D::check_filename(&cfg.filename) { println!("Found matching filename for D"); Box::new(B::from(cfg)) // 开发人员错误:构造了一个B而不是D。 }
- 向程序添加新的结构体(例如
E
、F
、G
等)不太方便。它需要在主if else
语句中为每个结构体添加一个新的分支。将结构体简单地添加到某种“主列表”中会更好。
有没有更优雅或习惯用语的方式来解决这些问题呢?
try_convert_config_to
函数中,T
需要具有'static
特性约束吗? - undefineddyn Foo
隐式地具有'static
约束,除非你指定一个,所以Box<dyn Runnable>
等同于Box<dyn Runnable + 'static>
。因此,要将Box<T>
转换为Box<dyn Runnable>
,T
必须是'static
。 - undefined'a
拥有Box<dyn Runnable + 'a>
。 - undefined