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

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

问题描述

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

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

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