0
点赞
收藏
分享

微信扫一扫

rust 继承,作为类型系统与代码共享

M4Y 03-08 22:30 阅读 2

继承(Inheritance)是一个很多编程语言都提供的机制,一个对象可以定义为继承另一个对象的定义,

这使其可以获得父对象的数据和行为,而无需重新定义。

如果一个语言必须有继承才能被称为面向对象语言的话,那么 Rust 就不是面向对象的。无法定义一个结

构体继承父结构体的成员和方法。然而,如果你过去常常在你的编程工具箱使用继承,根据你最初考虑

继承的原因,Rust 也提供了其他的解决方案。

选择继承有两个主要的原因。第一个是为了重用代码:一旦为一个类型实现了特定行为,继承可以对一

个不同的类型重用这个实现。相反 Rust 代码可以使用默认 trait 方法实现来进行共享,在示例 10-14 中

我们见过在 Summary trait 上增加的 summarize 方法的默认实现。任何实现了 Summary trait 的类型

都可以使用 summarize 方法而无须进一步实现。这类似于父类有一个方法的实现,而通过继承子类也

拥有这个方法的实现。当实现 Summary trait 时也可以选择覆盖 summarize 的默认实现,这类似于子

类覆盖从父类继承的方法实现。

第二个使用继承的原因与类型系统有关:表现为子类型可以用于父类型被使用的地方。这也被称为 多态

(polymorphism),这意味着如果多种对象共享特定的属性,则可以相互替代使用。

多态(Polymorphism)

很多人将多态描述为继承的同义词。不过它是一个有关可以用于多种类型的代码的更广泛的概念。

对于继承来说,这些类型通常是子类。Rust 则通过泛型来对不同的可能类型进行抽象,并通过 trait

bounds 对这些类型所必须提供的内容施加约束。这有时被称为 bounded parametric polymorphism。

近来继承作为一种语言设计的解决方案在很多语言中失宠了,因为其时常带有共享多于所需的代码的风

险。子类不应总是共享其父类的所有特征,但是继承却始终如此。如此会使程序设计更为不灵活,并引

入无意义的子类方法调用,或由于方法实际并不适用于子类而造成错误的可能性。某些语言还只允许子

类继承一个父类,进一步限制了程序设计的灵活性。

因为这些原因,Rust 选择了一个不同的途径,使用 trait 对象而不是继承。让我们看一下 Rust 中的 trait

对象是如何实现多态的。

为使用不同类型的值而设计的 trait 对象

我们谈到了 vector 只能存储同种类型元素的局限。示例 8-10 中提供了一个定义

SpreadsheetCell 枚举来储存整型,浮点型和文本成员的替代方案。这意味着可以在每个单元中储

存不同类型的数据,并仍能拥有一个代表一排单元的 vector。这在当编译代码时就知道希望可以交替使

用的类型为固定集合的情况下是完全可行的。

然而有时我们希望库用户在特定情况下能够扩展有效的类型集合。为了展示如何实现这一点,这里将创

建一个图形用户接口(Graphical User Interface,GUI)工具的例子,它通过遍历列表并调用每一个项

目的 draw 方法来将其绘制到屏幕上 —— 此乃一个 GUI 工具的常见技术。我们将要创建一个叫做 gui

的库 crate,它含一个 GUI 库的结构。这个 GUI 库包含一些可供开发者使用的类型,比如 Button 或

TextField。在此之上,gui 的用户希望创建自定义的可以绘制于屏幕上的类型:比如,一个程序员可能

会增加 Image,另一个可能会增加 SelectBox。

这个例子中并不会实现一个功能完善的 GUI 库,不过会展示其中各个部分是如何结合在一起的。编写库

的时候,我们不可能知晓并定义所有其他程序员希望创建的类型。我们所知晓的是 gui 需要记录一系列

不同类型的值,并需要能够对其中每一个值调用 draw 方法。这里无需知道调用 draw 方法时具体会发

生什么,只要该值会有那个方法可供我们调用。

在拥有继承的语言中,可以定义一个名为 Component 的类,该类上有一个 draw 方法。其他的类比如

Button、Image 和 SelectBox 会从 Component 派生并因此继承 draw 方法。它们各自都可以覆盖 draw

方法来定义自己的行为,但是框架会把所有这些类型当作是 Component 的实例,并在其上调用 draw。

不过 Rust 并没有继承,我们得另寻出路。

举报

相关推荐

0 条评论