每日一 R「07」类型系统(一)
在前面的课程中,我们学习了变量所有权、借用以及生命周期等 Rust 中非常重要且比较难懂的内容。今天我们将开始跟着老师一起学习 Rust 中的类型系统。
01-类型系统基本概念
从机器码角度是不存在类型的,与指令交互的是寄存器或内存中的数据流。
类型系统是高级语言中的概念,它是对值的区分,包含了值在内存中的长度、对齐方式以及值允许的操作方式等。因此,类型系统可以看作是一种工具,在编译期的静态检查和运行期的动态检查中,来保证处理的数据是开发者期望的类型。
类型系统是对类型进行定义、检查和处理的系统。类型系统可以划分为:
按定义后类型是否可以隐式转换,类型系统可分为强类型系统和弱类型系统,前者不允许隐式转换,后者允许隐式转换。
按照类型检查的时机,可以分为静态类型系统和动态类型系统,前者检查发生在编译期,后者检查发生在运行期。
类型系统中有一个非常重要的概念,多态,即在使用相同类型的接口时,不同类型的对象,会采用不同的实现。不同的类型系统实现多态的方式也不尽相同:
对于动态类型系统,多态通过鸭子类型(duck type)实现。
对于静态类型系统,多态通过参数多态、特设多态和子类型多态实现。
其中,参数多态指,函数操作的是满足某个约束(例如实现了某个 trait)的参数,而非具体的类型;
特设多态是指同一种行为(例如加法)可以有多个不同实现的多态,例如面向对象语言中的重载;
子类型多态指运行时子类型可以被当成父类型使用,例如面向对象语言中的里氏替换原则。
01.1-Rust 中多态是如何实现的
Rust 是一个静态强类型语言,它对参数多态的支持通过泛型来实现,对特设多态的支持通过 trait 来实现,对子类型多态的支持通过 trait object 来实现。
类型安全,从内存角度看,是指代码只能按照被允许的方法、访问它被授权访问的内存。Rust 下,类型安全有更严格地限制,即代码只能按照被允许的方法和被允许的权限,访问它被授权访问的内存。
为实现如此严格的要求,Rust 中规定一切代码块(出了 let 等定义性语句外)都是表达式。表达式是有类型的,所以类型无处不在。表达式是能够计算出值的,那下面这段表达式的值是什么?
Rust 中表达式的值是代码块中最后一个表达式的值。上述代码中,第一个 if 的返回值是 ()。() 是 Rust 中的一个特别的元素,也称为 unit,它的类型为 (),值也为 (),且在内存中并不占空间。第二个 if 的返回值是 30。
02-泛型(参数多态)
02.1-泛型数据结构
常见的泛型数据结构:Option<T>、Result<T, E>、Cow<T>
Option<T> 中的 T 未作任何约束,即任何类型都可以。 Cow<T> 中 B 是有约束的,出了必须具备申明周期 ‘a 外,还需要满足:实现了 ToOwned trait,可以是 ?Size 可变大小的类型。
02.2-泛型函数
与泛型数据结构一样,泛型参数需要提前声明,即 impl<T, U>。此时的 Point<T, U> 已不再是泛型声明,而是一个具体的数据结构。
02.3-单态化
Rust 在编译期间会将泛型转换为具体地类型,这个过程称为单态化。单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。正是单态化使得 Rust 运行时的效率不会丧失,但同时带来的是编译时间变长、可执行文件体积变大。
以标准库中 Option<T> 为例,如果按照如下方式使用:
经过编译编译,会产生如下对应的代码,这个过程就是单态化过程:
本节课程链接:《12|类型系统:Rust的类型系统有什么特点?》
历史文章推荐
版权声明: 本文为 InfoQ 作者【Samson】的原创文章。
原文链接:【http://xie.infoq.cn/article/bf1266d76d8a72b50dd078e1f】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论