如何避免将具体结构更改为泛型而产生连锁反应? [英] 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.
推荐答案
您可以使用特征对象 Box<dyn ListBuilder>
隐藏构建器的类型.一些后果是动态调度(对build
方法的调用将通过虚拟函数表进行),其他内存分配(装箱的特征对象)以及一些
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屋!