如何避免将具体结构更改为通用结构的连锁反应? [英] How can I avoid a ripple effect from changing a concrete struct to generic?

查看:18
本文介绍了如何避免将具体结构更改为通用结构的连锁反应?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆