Generics 如何避免将具体结构更改为泛型时产生连锁反应?

Generics 如何避免将具体结构更改为泛型时产生连锁反应?,generics,rust,traits,Generics,Rust,Traits,我有一个如下所示的配置结构: struct Conf { list: Vec<String>, } 这似乎很好,但现在无论我在哪里使用Conf,我都必须更改它: fn do_something(c: &Conf) { // ... } 变成 fn do_something<T>(c: &Conf<T>) where T: ListBuilder, { // ... } fn做某事(c:&Conf) 哪里 T

我有一个如下所示的配置结构:

struct Conf {
    list: Vec<String>,
}
这似乎很好,但现在无论我在哪里使用
Conf
,我都必须更改它:

fn do_something(c: &Conf) {
    // ...
}
变成

fn do_something<T>(c: &Conf<T>)
where
    T: ListBuilder,
{
    // ...
}
fn做某事(c:&Conf)
哪里
T:ListBuilder,
{
// ...
}
由于我有很多这样的函数,这种转换是痛苦的,特别是因为
Conf
类的大多数用法都不关心
ListBuilder
——这是一个实现细节。我担心的是,如果我向
Conf
添加另一个泛型类型,现在我必须返回并在所有地方添加另一个泛型参数。有没有办法避免这种情况

我知道我可以使用闭包来代替列表生成器,但我有一个附加的约束,即我的
Conf
结构需要是
Clone
,而实际的生成器实现更复杂,并且在生成器中有几个函数和一些状态,这使得闭包方法变得笨拙。

您可以使用
框来隐藏生成器的类型。一些结果是动态分派(对
build
方法的调用将通过虚拟函数表)、额外的内存分配(装箱的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
    }
}
trait列表生成器{
fn构建(&self,列表:&mut-Vec);
}
结构形态{
名单:Vec,,
建筑商:盒子,
}
impl-Conf{
fn初始值(&mut self){
self.builder.build(&mut-self.list);
}
}
impl-Conf{
pub fn new您可以使用
隐藏生成器的类型。其中一些后果是动态调度(调用
build
方法将通过虚拟函数表)、额外的内存分配(装箱的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
    }
}
trait列表生成器{
fn构建(&self,列表:&mut-Vec);
}
结构形态{
名单:Vec,,
建筑商:盒子,
}
impl-Conf{
fn初始值(&mut self){
self.builder.build(&mut-self.list);
}
}
impl-Conf{

pub fn new虽然泛型类型似乎会“感染”代码的其余部分,但这正是它们有益的原因!编译器对使用的类型有多大以及具体是什么类型的了解,使其能够做出更好的优化决策

也就是说,这可能很烦人!如果您有少量实现您的trait的类型,您还可以构造这些类型的枚举并委托给子实现:

enum MyBuilders{
用户(FromUser),
文件(FromFile),
}
MyBuilders的impl ListBuilder{
fn构建(自身,列表:&mut Vec){
使用MyBuilder::*;
匹配自我{
用户(u)=>u.build(列表),
文件(f)=>f.build(列表),
}
}
}
//支持代码
特征列表生成器{
fn构建(&self,列表:&mut-Vec);
}
来自用户的结构;
用于FromUser的impl ListBuilder{
fn构建(&self,list:&mut-Vec){}
}
struct FromFile;
用于FromFile的impl ListBuilder{
fn构建(&self,list:&mut-Vec){}
}
现在,具体类型将是
Conf
,您可以使用类型别名来隐藏它

当我希望能够在测试期间将测试实现注入到代码中,但在生产代码中使用了一组固定的实现时,我就使用了这个方法


有助于构建此模式。

虽然泛型类型似乎会“感染”代码的其余部分,但这正是它们有益的原因!编译器对使用的类型有多大以及具体是什么类型的了解,使其能够做出更好的优化决策

也就是说,这可能很烦人!如果您有少量实现您的trait的类型,您还可以构造这些类型的枚举并委托给子实现:

enum MyBuilders{
用户(FromUser),
文件(FromFile),
}
MyBuilders的impl ListBuilder{
fn构建(自身,列表:&mut Vec){
使用MyBuilder::*;
匹配自我{
用户(u)=>u.build(列表),
文件(f)=>f.build(列表),
}
}
}
//支持代码
特征列表生成器{
fn构建(&self,列表:&mut-Vec);
}
来自用户的结构;
用于FromUser的impl ListBuilder{
fn构建(&self,list:&mut-Vec){}
}
struct FromFile;
用于FromFile的impl ListBuilder{
fn构建(&self,list:&mut-Vec){}
}
现在,具体类型将是
Conf
,您可以使用类型别名来隐藏它

当我希望能够在测试期间将测试实现注入到代码中,但在生产代码中使用了一组固定的实现时,我就使用了这个方法


帮助构建此模式。

谢谢,这正是我所需要的。我的ListBuilder还需要实现克隆,我用这里描述的方法解决了这个问题:谢谢,这正是我所需要的。我的ListBuilder还需要实现克隆,我用这里描述的方法解决了这个问题:不需要有e绑定
大小
;这是默认值。不要求绑定
大小
;这是默认值。