【Rust精华小册】6. Rust 语言的类型系统

339 阅读5分钟

本节内容想为你建立一个完善的类型认知,不知道能不能达到效果。如果有不能理解的地方可以跳过,等学完全部内容后再看这一节。看大家的反馈吧,实在难以读懂,我就调整一下顺序。

一门编程语言在设计之初,需要考虑众多因素,其中设计类型系统就是非常重要的一环。

类型系统发展

早期的编程语言,例如 C 语言,使用简单的数据类型,例如整数、浮点数和字符。

随着编程语言的发展,出现了面向对象的编程语言,例如 C++ 和 Java。面向对象的编程语言引入了类和接口等抽象类型,可以描述更复杂的概念。

近年来,出现了函数式编程语言,例如 Haskell 和 Scala。函数式编程语言强调类型系统的表达力,并引入了类型推断等技术。

如何设计类型系统

类型系统主要解决下面两个问题:

  1. 语言的类型有哪些? 类似一次能源、二次能源,我们把类型分为一次类型和二次类型。一次类型就是语言内置类型,二次类型是基于一次类型创建的,包括标准库或用户自定义的类型。此外还有一种是抽象类型,仅用于描述类型的行为,如特征和泛型。

  2. 如何检查类型错误? Rust 类型系统通过编译时检查和运行时检查来确保类型安全。

Rust 编译器会在编译时进行类型检查,以确保以下内容:

  • 变量的类型声明与赋值表达式匹配。
  • 函数参数的类型与调用表达式匹配。
  • 返回值的类型与函数声明匹配。
  • 所有权和借用规则得到遵守。

例如,以下代码会导致编译错误:

let x: i32 = 1.0; // 无法将浮点数赋值给 i32 变量

Rust 运行时也会进行一些类型检查,以确保以下内容:

  • 空指针访问不会发生。
  • 数组越界访问不会发生。
  • 数据竞争不会发生。

例如,以下代码会导致运行时错误:

let mut x = [1, 2, 3];
let y = x[4]; // 数组越界访问

Rust 中类型错误的常见原因:

  • 变量类型声明错误。
  • 函数参数类型声明错误。
  • 返回值类型声明错误。
  • 所有权和借用规则使用错误。
  • 类型转换错误。

类型系统的本质

类型系统是编程语言中用于定义和控制类型的一组规则。包括定义类型、类型之间的交互、类型检查和类型推断这四部分。

1. 类型定义

类型是用来描述值的性质和行为的抽象概念。

  • 一次类型:整数、浮点数、字符、bool、指针
  • 二次类型:数组、切片、元组、结构体、枚举、函数、闭包
  • 抽象类型:trait、泛型

类型分类的方式有很多,例如原始类型和自定义类型等。原始类型可以查看Rust官方文档:doc.rust-lang.org/std/index.h…

2. 类型规则

类型规则定义了类型之间如何相互作用。例如,以下类型规则定义了赋值操作:

赋值操作的左值和右值的类型必须一致。

3. 类型检查

类型检查是用来确保程序遵循类型规则的过程。类型检查可以在编译时或运行时进行。

所有权、借用、生命周期就是编译器进行类型检查的规则。我们在上节内存释放时见到过,这些重点我们会在学完类型系统以后介绍它们。

4. 类型推断

类型推断是一种自动推断变量类型的技术。类型推断可以使代码更加简洁易读。

我们来看一些类型推断的例子:

/// 1. **变量声明**
let x = 1; // 编译器推断 x 的类型为 i32

/// 2. **函数参数**
fn add_one(x: i32) -> i32 {
  x + 1
}

let result = add_one(1); // 编译器推断 result 的类型为 i32

/// 3. **函数返回值**
fn get_string() -> String {
  "hello".to_string()
}

let s = get_string(); // 编译器推断 s 的类型为 String

/// 4. **运算符**
let x = 1 + 2; // 编译器推断 x 的类型为 i32

/// 5. **模式匹配**
let x = Some(1);

match x {
  Some(n) => println!("{}", n),
  None => println!("None"),
}

总结

一块内存就像陶泥,没有经过塑形它可以是任何形状。如果把它做成一个杯子,用它装水,装油,甚至还可以装泥巴,但是你不能用杯子切菜。 对于一块内存在没有类型约束时,你可以对他做任何操作,但通常没有任何意义。为内存赋予一个类型,例如i32类型,一旦赋予了类型那么能够对这块内存施加的操作也就定下来了,i32类型允许加减等操作,但你要对这块内存做字符串拆分操作是不允许的,编译器会报错。经过编译步骤,对内存类型检查后,程序程序运行时对这块内存的操作也就固定了,CPU才不管这四个字节是什么类型,只是按照指令操作这块内存。因此,我们说类型只是高级语言对内存的一种抽象,在计算机硬件层没有类型的概念,硬件眼里只有二进制。

学完这一章,你一定会直呼高级语言是管理内存的艺术,那我们赶快进入下一节看看具体的类型。