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