· 开发

理解 Rust 类型系统 #0

作为系列文章的开始,本文整理了 Rust 的类型系统。

所有内容参考 https://doc.rust-lang.org/reference/types.html,但分类标准略有不同。增加了部分注释。

基本数据类型

类型名称 大小 说明
bool 1 只能为逻辑值 truefalse
i8/i16/i32/i64 1/2/4/8 8/16/32/64 位有符号整数,采用补码表示法
u8/u16/u32/u64 1/2/4/8 8/16/32/64 位无符号整数,采用补码表示法
isize/usize 平台相关 有/无符号整数,大小与体系结构规定的指针相等,至少为16位1
f32/f64 4/8 IEEE-754 单/双精度浮点数
char 4 Unicode 标量值,范围在 U+0 ~ U+D7FF 和 U+E000 ~ U+10FFFF(含两端)内,以 32 位无符号整数形式表示
str 动态 (DST) 采用 UTF-8 编码的合法字符串,以 [u8] 数组形式表示2
! (never) - 发散函数的返回值类型,不能定义实例,没有有效取值

积类型

元组 (tuple)

由任意多个同种或异种类型的元素捆绑组成的类型,根据用户指定的元组成员顺序,每个元素与相应的顺序号 0、1、2……绑定。

支持空元组 (),空元组又称为单元类型 (unit type)。

注意 Rust 并不保证元组的物理存储顺序,有可能与元素的顺序号不同。

结构体 (struct)

由任意多个同种或异种类型的元素捆绑组成的类型,每个元素各自与一个名称(结构体成员)绑定。支持空结构体 StructType {}

与元组相似,Rust 不保证结构体成员的物理存储顺序。

数组 (array)

由固定个数的同种类型的元素按顺序排列组成的类型。

数组的所有成员在内存中总是按顺序紧接着存储,对于数组类型 [T; N],总是可以在数组起始位置上增加偏移量 n * size_of::<T> 来找到第 n 个元素。

切片 (slice)

切片类型是动态大小类型(DST),表示一组同种元素组成的序列的「视图」。通常与指针类型结合使用。

和类型

枚举 (enum)

与 C 语言的 union 类似,但是各种可能的类型通过标记(枚举值)区分。

每个成员可以是结构体、元组或单元类型。如果所有成员均为单元类型,则与 C 语言的枚举等同。

由于 Rust 的枚举可通过标记区分到底是哪一种成员的值,因此访问枚举成员是安全的。

联合 (union)

与 C 语言的 union 相同,可在多种类型的合法值集合的并集中取值。

编译器无法通过类型系统确定一个 union 具体取了哪种成员的值,因此通过 union 成员取值是不安全的。

函数类型

函数 (function)

通过 fn 关键字定义的函数的类型。每个通过 fn 定义的函数都具有唯一对应的函数类型3

注意,函数的名称和泛型参数也都是函数类型的一部分。函数类型可以隐式转换为函数指针类型,此时会舍去函数名称部分。

两个不同名但参数列表和返回值类型都相同的函数,属于不同类型,但可以隐式转换为相同的函数指针类型。

闭包 (closure)

通过闭包表达式定义的闭包所具有的匿名类型,无法在源代码中显式写出。类似于带有捕获变量、定义了调用方法的结构体。

指针类型

引用 (reference)

通过 &&mut 标记(分别表示不可以/可以通过该引用修改被引用的值)指定的变量引用。

裸指针 (raw pointer)

通过 *const*mut 标记(分别表示不可以/可以通过该指针修改指向的值)指定的裸指针。解引用裸指针是不安全操作。

函数指针 (function pointer)

通过参数列表、返回值类型和调用约定指定的函数指针。

当且仅当指定了使用 C 语言调用约定(extern "C"extern "cdecl")时,可以使用可变参数。

特征类型

特征对象 (trait object)

通过 dyn 关键字加一个或多个特征约束(其中至多有一个非自动特征 (non-auto trait))以及可选生命周期约束指定的类型。特征对象自动实现了所有在约束中指定的特征。

特征对象的值可以是任何满足全部约束的类型的值,因此特征对象类型是动态大小类型,通常结合指针类型使用。此时特征对象指针是一个胖指针 (fat pointer),包括一个指向实际数据的指针和相应特征的虚函数表,可以实现后期绑定。

匿名参数类型/抽象返回类型 (impl trait)

通过 impl 关键字加一个或多个特征约束指定的匿名类型,可以出现在函数参数或函数返回值处。

出现在函数参数时,语义与泛型参数类似,但调用者无法使用 Turbofish 语法强制指定实际的参数类型。

出现在返回值时,表示一个抽象返回类型(可能由于实际的返回类型名称过长,或者希望通过返回类型提示相应的接口、但不希望调用者关心具体实现)。函数实现需要给出函数的实际返回值,这个实际返回值的类型将是抽象返回类型对应的实际返回类型。

  1. 参考 https://doc.rust-lang.org/reference/types/numeric.html#r-type.numeric.int.size.minimum。不能简单假定为 32 位或 64 位(尽管非 32/64 位的平台很少见)。

  2. 与 C/C++ 不同,Rust 并不保证字符串末尾一定有 NUL 字符 '\0'

  3. https://doc.rust-lang.org/reference/types/function-item.html#r-type.fn-item.unique

理解 Rust 类型系统 #0