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绑定大小
;这是默认值。不要求绑定大小
;这是默认值。