我对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你有一个操场的替代版本。
1条答案
按热度按时间mv1qrgav1#
是的,编译器会为每种类型生成一个
add_val()
的新示例。如果它们使用相同的机器码,编译器可能会将它们折叠,但如果它们不使用相同的机器码,编译器就不会将它们折叠。如果你想避免这种情况,一个常见的方法是定义一个嵌套函数(参见Why does std::fs::write(...) use an inner function?):然而,你所做的并不是组合,而是用组合来模拟继承。(通常)具有
AsBase
特性,因为您不应该将DerivedStruct
视为BaseStruct
。这不是“is-a”关系,而是“has-a”关系。如果方法需要BaseStruct
,直接向它传递一个对字段的引用并让它执行自己的工作。