为什么Rust不支持特征对象向上转换?
鉴于此代码:
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
在这样或那样的方法。
检查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,
所有Rust vtable都包含指向第一个字段中的析构函数,大小和对齐的指针,并且子引用vtable在引用supertrait方法时不会复制它们,也不会使用对supertrait vtables的间接引用。他们只是逐字地拥有方法指针的副本而没有别的。
鉴于这种设计,很容易理解为什么这不起作用。需要在运行时构建一个新的vtable,它可能存在于堆栈中,这并不是一个优雅(或最佳)的解决方案。
当然,有一些解决方法,比如在界面中添加显式的upcast方法,但这需要相当多的样板(或宏观狂热)才能正常工作。
现在,问题是 - 为什么不能以某种方式实现特征对象向上转换?比如,在subtrait的vtable中添加指向supertrait的vtable的指针。目前,Rust的动态调度似乎不满足Liskov替换原则,这是面向对象设计的一个非常基本的原则。
当然你可以使用静态调度,这在Rust中使用确实非常优雅,但它很容易导致代码膨胀,这有时比计算性能更重要 - 比如在嵌入式系统上,而Rust开发人员声称支持这样的用例。语言。此外,在许多情况下,您可以成功使用一个非纯粹面向对象的模型,这似乎是Rust的功能设计所鼓励的。尽管如此,Rust支持许多有用的OO模式......那么为什么不使用LSP呢?
有谁知道这种设计的理由?
杨魅力