我有一个如下所示的配置结构:
struct Conf {
list: Vec<String>,
}
该实现在内部填充了 list
成员,但现在我决定将该任务委托(delegate)给另一个对象。所以我有:
trait ListBuilder {
fn build(&self, list: &mut Vec<String>);
}
struct Conf<T: Sized + ListBuilder> {
list: Vec<String>,
builder: T,
}
impl<T> Conf<T>
where
T: Sized + ListBuilder,
{
fn init(&mut self) {
self.builder.build(&mut self.list);
}
}
impl<T> Conf<T>
where
T: Sized + ListBuilder,
{
pub fn new(lb: T) -> Self {
let mut c = Conf {
list: vec![],
builder: lb,
};
c.init();
c
}
}
这似乎工作正常,但现在到处我使用Conf
,我必须改变它:
fn do_something(c: &Conf) {
// ...
}
成为
fn do_something<T>(c: &Conf<T>)
where
T: ListBuilder,
{
// ...
}
由于我有很多这样的函数,这种转换很痛苦,尤其是因为 Conf
类的大多数用法并不关心 ListBuilder
- 它是一个实现细节。我担心如果我向 Conf
添加另一个泛型类型,现在我必须返回并在所有地方添加另一个泛型参数。有什么办法可以避免这种情况吗?
我知道我可以使用闭包代替列表生成器,但我有一个额外的约束,即我的 Conf
结构需要是 Clone
,而实际的生成器实现更复杂,并且在构建器中有多个函数和一些状态,这使得闭包方法变得笨拙。
最佳答案
虽然泛型类型似乎可以“感染”您的其余代码,但这正是它们有益的原因!编译器关于使用的大小和具体类型的知识使其能够做出更好的优化决策。
话虽如此,这可能很烦人!如果您有少量实现特征的类型,您还可以构建这些类型的枚举并委托(delegate)给子实现:
enum MyBuilders {
User(FromUser),
File(FromFile),
}
impl ListBuilder for MyBuilders {
fn build(&self, list: &mut Vec<String>) {
use MyBuilders::*;
match self {
User(u) => u.build(list),
File(f) => f.build(list),
}
}
}
// Support code
trait ListBuilder {
fn build(&self, list: &mut Vec<String>);
}
struct FromUser;
impl ListBuilder for FromUser {
fn build(&self, list: &mut Vec<String>) {}
}
struct FromFile;
impl ListBuilder for FromFile {
fn build(&self, list: &mut Vec<String>) {}
}
现在具体类型是Conf<MyBuilders>
,您可以使用类型别名来隐藏它。
当我希望能够在测试期间将测试实现注入(inject)到代码中时,我使用它取得了很好的效果,但在生产代码中使用了一组固定的实现。
enum_dispatch crate有助于构建这种模式。
关于generics - 如何避免将具体结构更改为通用结构所产生的链式 react ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56139786/