说明白 Rust 中的泛型: 泛型是一种多态

泛型(Generics) 这一概念比较古老,从 80 年代末的 Ada 开始一直到现在成为了静态类型语言的标配,包括 Haskell、C++、Java、Go、Rust。而这系列文章主要描述 Rust 中的泛型,对其他语言并不会过多涉及。

泛型是一种多态

泛型是一种多态(Polymorphism), 具体来说是参数多态的一种实现。那么多态又是什么呢?对于它的解释经常存在歧义,原因是多态有很多不同的类型。

  1. 参数多态(Parametric Polymorphism): 比如 Rust 的泛型就是一种参数多态,在编译时就会确定具体的类型,采用单态化(Monomorphization)的方式生成代码,运行时不会存在任何的多态行为,因此也不存在任何的性能损失。Java 的实现方式不一样,采用的是编译时类型擦除,这就导致了运行时是无法获取到完整的类型信息的,只能通过反射获取部分信息。

  2. 子类型多态(Subtype Polymorphism): 一般是运行时多态,比如在 OOP 中谈论到多态,父类型的引用可以指向子类型的实例。可能会采用虚表(vtable)的方式来实现,每一个类都有一张方法表,调用时通过这张表来查找具体的函数地址。存在运行时开销。在 Rust 中,这类多态是通过 Trait Object 来实现的。

  3. 重载多态(Ad-hoc Polymorphism): 比如方法重载、操作符重载都属于这一类,根据方法或者操作符不同实现多份代码。这类多态表现为函数签名不同或者参数类型不同。

不同语言中对泛型的不同实现

需要额外说明的是,Java 和 Rust 都支持参数多态和子类型多态。比如 Rust 的 Trait Object 就是运行时多态。两种语言的示例如下:

java 复制代码
List<String> list = new ArrayList();    // 参数多态
Animal animal = new Dog();              // 子类型多态

我们再来看 Rust 中的多态:

rust 复制代码
fn print<T: Debug>(x: T) {}     // 参数多态
fn print(x: &dyn Debug) {}      // 子类型多态

在动态类型的语言中,比如 Python、Ruby、PHP、JavaScript 是通过鸭子类型(Duck Typing)来实现多态的。我们来看一个 Python 的例子:

python 复制代码
def print_length(obj):
    print(len(obj))
print_length([1, 2, 3])  # 传入数组可以
print_length("hello")    # 传入字符串也可以
print_length(100)        # 运行时报错,因为数值类型没有 len()

动态类型的语言中,非常灵活,不需要特别声明类型。但是,鸭子类型也存在一些问题,比如在编译时无法检查类型,运行时会报错, 这种类型的错误需要通过更多的单元测试、边界测试才能发现。这种灵活性,对开发者的要求也会更高,当然自由度更高。

现代的一些编程语言,也从弱类型语言中吸取其灵活的特性,比如鸭子类型。在 GO 语言中的接口,你也不需要声明实现了这个接口,只要实现了接口所需的方法后就可以作为出参数传入。Rust 中为了提升灵活性,引入了 trait object,这些都是"动态语言"的优点的体现,灵活性。但是会存在运行时开销。

下面这个例子演示了 Go 语言中静态的鸭子类型的案例, 兼顾了性能和灵活性:

go 复制代码
type Speaker interface {
   Speak()
}
type Dog struct{}
func (d Dog) Speak() {
   fmt.Println("汪汪!")
}
func main() {
   dog := Dog{}
   // 由于 Dog 实现了 Speak 方法
   // 它们都可以作为 Speaker 类型参数传递
   makeSound(dog)
}

其实静态类型的语言和动态类型的语言现在是相互学习,静态类型的学习动态性,而动态类型的语言则学习静态型。比如 PHP 、Python 在完善类型系统,而 Go、Rust 、C++ 则通过动态分发来提升灵活性。

总结

在对比了一些其他语言之后,我们回过头来看 Rust 中的泛型,本质就是一种参数多态,将类型作为一种参数传入。因为是编译时多态,相比动态语言更加的安全和更高的性能,也兼顾了灵活性。就算相比其他静态类型的语言,Rust 的泛型有自己的很多特色,后续的文章详细分析。

相关推荐
红尘散仙18 分钟前
四、WebGPU 基础入门——Uniform 缓冲区与内存对齐
前端·rust·gpu
摘星编程28 分钟前
并发设计模式实战系列(6):读写锁
java·设计模式·并发编程
Allen Bright3 小时前
【设计模式-4】深入理解设计模式:工厂模式详解
设计模式
大卫小东(Sheldon)3 小时前
魔方求解器桌面版(层先法,基于Tauri实现)
rust
碎梦归途3 小时前
23种设计模式-结构型模式之适配器模式(Java版本)
java·开发语言·jvm·单例模式·设计模式·适配器模式
风,停下4 小时前
C#基于Sunnyui框架和MVC模式实现用户登录管理
设计模式·c#·mvc
景天科技苑6 小时前
【Rust结构体】Rust结构体详解:从基础到高级应用
开发语言·后端·rust·结构体·关联函数·rust结构体·结构体方法
Pasregret6 小时前
中介者模式:解耦对象间复杂交互的设计模式
设计模式·交互·中介者模式
bruce541108 小时前
【智能指针】一文带你入门Rust 智能指针
rust