如何避免将具体结构更改为通用结构的连锁反应? [英] How can I avoid a ripple effect from changing a concrete struct to generic?
问题描述
我有一个如下所示的配置结构:
I have a configuration struct that looks like this:
struct Conf {
list: Vec<String>,
}
该实现在内部填充 list
成员,但现在我决定将该任务委托给另一个对象.所以我有:
The implementation was internally populating the list
member, but now I have decided that I want to delegate that task to another object. So I have:
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
,我必须改变它:
That seems to work fine, but now everywhere that I use Conf
, I have to change it:
fn do_something(c: &Conf) {
// ...
}
变成
fn do_something<T>(c: &Conf<T>)
where
T: ListBuilder,
{
// ...
}
因为我有很多这样的函数,这种转换很痛苦,尤其是因为 Conf
类的大多数用法都不关心 ListBuilder
- 这是一个实现细节.我担心如果我向 Conf
添加另一个泛型类型,现在我必须返回并在任何地方添加另一个泛型参数.有什么办法可以避免这种情况吗?
Since I have many such functions, this conversion is painful, especially since most usages of the Conf
class don't care about the ListBuilder
- it's an implementation detail. I'm concerned that if I add another generic type to Conf
, now I have to go back and add another generic parameter everywhere. Is there any way to avoid this?
我知道我可以使用闭包来代替列表构建器,但是我有一个附加的约束,即我的 Conf
结构需要是 Clone
,以及实际的构建器实现更复杂,并且在构建器中有多个函数和一些状态,这使得闭包方法变得笨拙.
I know that I could use a closure instead for the list builder, but I have the added constraint that my Conf
struct needs to be Clone
, and the actual builder implementation is more complex and has several functions and some state in the builder, which makes a closure approach unwieldy.
推荐答案
您可以使用 trait 对象 Box
隐藏构建器的类型.一些后果是动态调度(对 build
方法的调用将通过一个虚函数表)、额外的内存分配(装箱的 trait 对象),以及一些 对特征的限制 ListBuilder
.
You can use the trait object Box<dyn ListBuilder>
to hide the type of the builder. Some of the consequences are dynamic dispatch (calls to the build
method will go through a virtual function table), additional memory allocation (boxed trait object), and some restrictions on the trait ListBuilder
.
trait ListBuilder {
fn build(&self, list: &mut Vec<String>);
}
struct Conf {
list: Vec<String>,
builder: Box<dyn ListBuilder>,
}
impl Conf {
fn init(&mut self) {
self.builder.build(&mut self.list);
}
}
impl Conf {
pub fn new<T: ListBuilder + 'static>(lb: T) -> Self {
let mut c = Conf {
list: vec![],
builder: Box::new(lb),
};
c.init();
c
}
}
这篇关于如何避免将具体结构更改为通用结构的连锁反应?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!