Oop 为什么';不生锈的支持特征对象向上投射?
鉴于此代码:Oop 为什么';不生锈的支持特征对象向上投射?,oop,rust,language-design,liskov-substitution-principle,Oop,Rust,Language Design,Liskov Substitution Principle,鉴于此代码: 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) {}
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]:非基元强制转换:`&Derived`as`&Base`
-->src/main.rs:30:5
|
30 | v组件和底座;
| ^^^^^^^^^^
|
=注意:`as`表达式只能用于在基元类型之间转换。考虑使用“从”特性
为什么呢?派生的
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
}
所有Rust vtable在第一个字段中都包含一个指向析构函数、大小和对齐方式的指针,subtrait vtable在引用supertrait方法时不会复制它们,也不会间接引用supertrait vtables。它们只有方法指针的逐字副本,没有其他内容
考虑到这种设计,很容易理解为什么这不起作用。需要在运行时构造一个新的vtable,它可能位于堆栈上,这并不是一个优雅(或最佳)的解决方案
当然,有一些变通方法,比如向接口添加显式的向上转换方法,但这需要相当多的样板文件(或宏狂热)才能正常工作
现在,问题是-为什么它不能以某种方式实现,从而支持trait对象的向上转换?比如,在子行的vtable中添加指向supertrait的vtable的指针。目前,Rust的动态调度似乎不满足,这是面向对象设计的一个非常基本的原则
当然,您可以使用静态分派,在Rust中使用它确实非常优雅,但它很容易导致代码膨胀,这有时比计算性能更重要,就像在嵌入式系统上一样,Rust开发人员声称支持这种语言的用例。此外,在许多情况下,您可以成功地使用一个并非纯粹面向对象的模型,这似乎是由Rust的功能设计所鼓励的。不过,Rust支持许多有用的OO模式。。。那么,为什么不是LSP呢
有人知道这种设计的基本原理吗?当我开始生锈的时候,我碰到了同一堵墙。 现在,当我想到特质时,我脑海中的形象与我想到班级时不同
trait X:Y{}
意味着当您为structS实现traitX
时,您还需要为S实现traitY
当然,这意味着&X
知道它也是&Y
,因此提供了适当的功能。
如果需要首先遍历指向Y
的vtable的指针,则需要一些运行时工作(更多的指针解引用)
再说一次,当前的设计+指向其他vtable的额外指针可能不会有太大的影响,并且可以实现轻松的强制转换。也许我们两个都需要?这是一个需要讨论的问题。事实上,我想我知道原因了。我发现了一种优雅的方法,可以向任何需要的特性添加向上转换支持,这样程序员就可以选择是向该特性添加额外的vtable条目,还是不添加,这与C++的虚拟与非虚拟方法中的权衡类似:优雅、模型正确性与性能
代码可以按如下方式实现:
trait Base: AsBase {
// ...
}
trait AsBase {
fn as_base(&self) -> &Base;
}
impl<T: Base> AsBase for T {
fn as_base(&self) -> &Base {
self
}
}
trait Base:AsBase{
// ...
}
性状AsBase{
fn as_base(&self)->&base;
}
用于T的impl AsBase{
fn as_base(&self)->&base{
自己
}
}
可以添加其他方法来强制转换&mut
指针或框
(这增加了T
必须是'静态
类型的要求),但这是一个一般想法。这允许安全、简单(尽管不是隐式)地向上投射每个派生类型,而不必为每个派生类型提供样板。截至2017年6月,这种“子特质强制”(或“超级特质强制”)的状态如下:
trait Base: AsBase {
// ...
}
trait AsBase {
fn as_base(&self) -> &Base;
}
impl<T: Base> AsBase for T {
fn as_base(&self) -> &Base {
self
}
}
- 一个公认的RFC提到这是胁迫的一部分。因此,这种转换应该隐式进行。
强制内部(
T
)=U
,其中T
是U
的一个子特征
- 然而,这一点尚未实施。有一个相应的问题
还有一个重复的问题。那里的评论解释了是什么阻止了这一计划的实施
- 基本上,问题是如何为超级性状导出vtable。vtables的当前布局如下(在x86-64情况下):
+-----+-------------------------------+
|0-7 |指向“滴胶”功能的指针|
+-----+-------------------------------+
|8-15 |数据的大小|
+-----+-------------------------------+
|16-23 |数据对齐|
+-----+-------------------------------+
|24-|自我和超特质的方法|
+-----+-------------------------------+
它不包含超级特征的vtable作为子序列。我们至少需要对vtables进行一些调整
- 当然,有很多方法可以缓解这个问题,但很多方法都有不同的优缺点!当存在菱形继承时,vtable大小有一个好处。另一个应该更快
有人说他们准备了一种看起来组织良好的语言,但在那之后它们似乎消失了(2016年11月)。作为旁注:Rust不是面向对象的语言。trait不是接口,它们更像来自Haskell的类型类。Rust也没有子类型,因此LSP在某种程度上不适用于它,因为它的定义与子类型关系有关。尽管如此,正如我所说,Rust支持很多OO风格的抽象,并且允许特征继承,形成类似于类型层次结构的东西。对我来说,支持trait对象的LSP似乎是很自然的,即使OO不是语言的主要范例