鉴于这一守则:
trait Base {
fn a(&self);
fn b(&self);
fn c(&self);
fn d(&self);
}
trait Derived : Base {
fn e(&self);
fn f(&self);
fn g(&self);
}
struct S;
impl Derived for S {
fn e(&self) {}
fn f(&self) {}
fn g(&self) {}
}
impl Base for S {
fn a(&self) {}
fn b(&self) {}
fn c(&self) {}
fn d(&self) {}
}不幸的是,我不能将&Derived转换为&Base
fn example(v: &Derived) {
v as &Base;
}error[E0605]: non-primitive cast: `&Derived` as `&Base`
--> src/main.rs:30:5
|
30 | v as &Base;
| ^^^^^^^^^^
|
= note: an `as` expression can only be used to convert between primitive types. Consider using the `From` trait为什么会这样呢?Derived vtable必须以某种方式引用Base方法。
检查LLVM IR显示如下:
@vtable4 = internal unnamed_addr constant {
void (i8*)*,
i64,
i64,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*
} {
void (i8*)* @_ZN2i813glue_drop.98717h857b3af62872ffacE,
i64 0,
i64 1,
void (%struct.S*)* @_ZN6S.Base1a20h57ba36716de00921jbaE,
void (%struct.S*)* @_ZN6S.Base1b20h3d50ba92e362d050pbaE,
void (%struct.S*)* @_ZN6S.Base1c20h794e6e72e0a45cc2vbaE,
void (%struct.S*)* @_ZN6S.Base1d20hda31e564669a8cdaBbaE
}
@vtable26 = internal unnamed_addr constant {
void (i8*)*,
i64,
i64,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*,
void (%struct.S*)*
} {
void (i8*)* @_ZN2i813glue_drop.98717h857b3af62872ffacE,
i64 0,
i64 1,
void (%struct.S*)* @_ZN9S.Derived1e20h9992ddd0854253d1WaaE,
void (%struct.S*)* @_ZN9S.Derived1f20h849d0c78b0615f092aaE,
void (%struct.S*)* @_ZN9S.Derived1g20hae95d0f1a38ed23b8aaE,
void (%struct.S*)* @_ZN6S.Base1a20h57ba36716de00921jbaE,
void (%struct.S*)* @_ZN6S.Base1b20h3d50ba92e362d050pbaE,
void (%struct.S*)* @_ZN6S.Base1c20h794e6e72e0a45cc2vbaE,
void (%struct.S*)* @_ZN6S.Base1d20hda31e564669a8cdaBbaE
}所有锈蚀表都包含一个指向第一个字段中析构函数、大小和对齐方式的指针,并且在引用超级属性方法时,子属性vtable不会复制它们,也不会使用对超级属性vtable的间接引用。它们只有方法指针的副本、逐字指针,而没有其他东西。
考虑到这种设计,很容易理解为什么这不起作用。需要在运行时构造一个新的vtable,它很可能驻留在堆栈中,而这并不是一个优雅(或最佳)的解决方案。
当然,也有一些解决办法,比如向接口中添加显式向上转换方法,但这需要相当多的样板(或宏狂热)才能正常工作。
现在,问题是--为什么它不以某种方式实现,从而使特性对象向上转换?例如,在子属性的vtable中添加指向超属性vtable的指针。目前,Rust的动态调度似乎不能满足Liskov代换原理,这是面向对象设计的一个非常基本的原则。
当然,您可以使用静态分派,这在Rust中确实非常优雅,但它很容易导致代码膨胀,这有时比计算性能更重要--比如在嵌入式系统上,而且Rust开发人员声称支持这种语言的用例。而且,在许多情况下,您可以成功地使用一个不仅仅是面向对象的模型,这似乎受到了Rust的功能设计的鼓励。不过,Rust支持许多有用的OO模式..。那么为什么不是LSP呢?
有谁知道这种设计的原理吗?
发布于 2022-10-29 13:00:17
现在正在处理稳定的锈蚀问题,您可以将其提升到基本特性,也可以从派生的属性对象直接调用基本特性函数。
trait Base {
fn a(&self) {
println!("a from base");
}
}
trait Derived: Base {
fn e(&self) {
println!("e from derived");
}
}
fn call_derived(d: &impl Derived) {
d.e();
d.a();
call_base(d);
}
fn call_base(b: &impl Base) {
b.a();
}
struct S;
impl Base for S {}
impl Derived for S {}
fn main() {
let s = S;
call_derived(&s);
}https://stackoverflow.com/questions/28632968
复制相似问题