Rust编译代码中的组合和可重用性

mxg2im7a  于 2022-11-12  发布在  其他
关注(0)|答案(1)|浏览(143)

我对Rust的组合方法是新的,我很难弄清楚我是否可以使我的代码更有效/更小。
让我们假设我有一个基本结构BaseStruct

struct BaseStruct {
   values: Vec<i32>,
}
impl BaseStruct {
    fn new() -> Self {
        Self{values: vec![]}
    }
}

然后,我定义了一个AsBase特征来简化组合过程:

/ Trait used to do composition
trait AsBase {
   fn as_base(&mut self) -> &mut BaseStruct;
   // Note that add_val has a default implementation!
   fn add_val(&mut self, val: i32) {
       // let us assume that this method has hundreds of lines of code
       self.as_base().values.push(val);
   }
}
// Note that BaseStruct implements the AsBase trait!
impl AsBase for BaseStruct {
    fn as_base(&mut self) -> &mut BaseStruct {
        self
    }
}

注意,BaseStruct实现了AsBase特征!否则,我们无法向向量添加一个值。然后,我使用composition导出了基本结构体的行为:

// Derived struct and handy functions
struct DerivedStruct {
   base: BaseStruct,
}
impl DerivedStruct {
    fn new() -> Self {
        Self{base: BaseStruct::new()}
    }
}

// Derived struct also implements the AsBase trait
impl AsBase for DerivedStruct {
    fn as_base(&mut self) -> &mut BaseStruct {
        &mut self.base
    }
}

现在,我可以使用trait方法向派生结构体的内部向量添加值了!

fn main() {
    let mut base = BaseStruct::new();
    base.add_val(1);

    let mut derived = DerivedStruct::new();
    derived.add_val(1); // With composition and AsBase trait, I achieve "inheritance"
}

Here这个例子有一个游戏场。
但是,如果add_val的默认方法非常复杂,需要数百行代码呢?Rust会为每个实现AsBase特征的结构体生成不同的add_val方法吗?或者编译器是否足够聪明,能够检测到它们可以共享同一个函数?
让我试着说得更清楚些:这个替代实现是否会更小,因为它显式地使用相同的方法?

// Base struct and handy associated methods
struct BaseStruct {
   values: Vec<i32>,
}
impl BaseStruct {
    fn new() -> Self {
        Self{values: vec![]}
    }
    fn add_val(&mut self, val: i32) {
    // Let us assume that this method is hundreds of lines long
        self.values.push(val);
    }
}

// Trait used to do composition
trait AsBase {
   fn as_base(&mut self) -> &mut BaseStruct;
   // Note that add_val has a default implementation!
   fn add_val(&mut self, val: i32) {
       self.as_base().add_val(val);
   }
}
// Note that BaseStruct does NOT implement the AsBase trait to avoid naming collision!

// Derived struct and handy functions
struct DerivedStruct {
   base: BaseStruct,
}
impl DerivedStruct {
    fn new() -> Self {
        Self{base: BaseStruct::new()}
    }
}

// Derived struct also implements the AsBase trait
impl AsBase for DerivedStruct {
    fn as_base(&mut self) -> &mut BaseStruct {
        &mut self.base
    }
}

fn main() {
    let mut base = BaseStruct::new();
    base.add_val(1);

    let mut derived = DerivedStruct::new();
    derived.add_val(1); // With composition and AsBase trait, I achieve "inheritance"
}

(Also,注意,由于命名冲突,我无法为BaseStruct实现AsBase特征,我不知道除了更改方法的名称之外,是否有其他解决方法可以避免这种情况)。
Here你有一个操场的替代版本。

mv1qrgav

mv1qrgav1#

是的,编译器会为每种类型生成一个add_val()的新示例。如果它们使用相同的机器码,编译器可能会将它们折叠,但如果它们不使用相同的机器码,编译器就不会将它们折叠。如果你想避免这种情况,一个常见的方法是定义一个嵌套函数(参见Why does std::fs::write(...) use an inner function?):

fn add_val(&mut self, val: i32) {
    fn inner(this: &mut BaseStruct) {
        // let us assume that this method has hundreds of lines of code
        base.values.push(val);
    }
    inner(self.as_base());
}

然而,你所做的并不是组合,而是用组合来模拟继承。(通常)具有AsBase特性,因为您不应该将DerivedStruct视为BaseStruct。这不是“is-a”关系,而是“has-a”关系。如果方法需要BaseStruct,直接向它传递一个对字段的引用并让它执行自己的工作。

相关问题