大家好,我是砸锅。一个摸鱼八年的后端开发。熟悉 Go、Lua。第二天还是继续学习 Rust
数据
数据是程序操作的对象
值
值是无法脱离具体的类型讨论的,不同类型下对应的含义不同
类型
类型是对值的区分,包含了值在内存的长度、对齐以及值可以进行的操作等信息
原生类型
原生类型就是编程语言提供的最基础的数据类型,比如字符串、整数、浮点数、布尔值等 所有原生类型的大小都是固定的,所以都可以被分配到栈上
组合类型
组合类型是指一组原生类型和其他类型组合而成的类型,组合类型也可以分为两种:
结构体 (structure type),多个类型组合在一起共同表达一个值的复杂数据结构。例如 Person struct,里面就包含 name,age,email 等信息- 标签联合 (tagged union),也叫不相交并集 (disjoint union),可以存储一组不同但固定的类型中的某个类型的对象,具体是哪个类型由其标签决定。之前没有接触过枚举的话,会有点难理解,可以利用扑克牌花色这个例子来理解:
enum PokerSuit {
Clubs,
Spades,
Diamonds,
Hearts,
}
任何一张扑克的花色都会在这四个花色其中,且只会是其中一个。这种特性特别适合用 enum,因为当用某个函数来处理扑克花色时,可以把它们当做相同的类型进行传参。总结下来:枚举类型是一个类型,包含所有可能的枚举成员,枚举值就是该类型中的具体某个成员实例
结构体其实是 Product type,也可以理解为逻辑与。标签联合是 Sum type,理解为逻辑或
指针
指针是一个包含了内存地址的变量,该内存地址引用或者指向了另外的数据
比正常指针携带更多信息的指针,称之为胖指针 (fat pointer),例如除了指针地址还包含了结构的一些信息
引用
常见的指针类型是引用,引用通过 & 符号表示,在 Rust 里可以理解为借用其他变量的值,但没有获取到所有权。也就是当引用离开作用域之后,并不会对指向的值造成影响。如果需要改变值,可以使用可变引用 &mut
可以通过解引用 (dereference) 来访问指针指向的内存地址,引用的解引用访问是有限制的,只能解引用到它引用数据的类型,不能做其他用途
指针和引用是原生类型,可以分配在栈上
代码
数据是程序操作的对象,代码就是程序运行的主体
函数
函数是编程语言的基本要素,是对代码中重复行为的抽象。在现代编程语言里,函数往往是第一公民。既可以当做参数传递,也可以作为返回值返回,也可以作为复合类型里的组成部分
在类或者对象中定义的函数,被称为方法 (method)
闭包 (closure) 则是将函数或者把代码和环境一起存储的一种数据结构,闭包引用的上下文中的自由变量,会被捕获到闭包的结构里,成为闭包类型的一部分
接口
接口反映了系统设计者对系统抽象理解,作为抽象层,接口是将使用方和实现方隔离开来,使两者不直接有依赖关系,大大提高了复用性和扩展性
当运行期使用接口来引用具体类型时,代码就具备了运行时多态的能力。但是在运行时,一旦使用了关于接口的引用,变量原本的类型被抹去,无法单纯从一个指针分析这个引用具备什么能力
类型擦除是一种通过抽象原理,对于不同类型但是满足相同接口的数据,抹去它们的原始类型,让他们拥有相同的接口类型,以便于统一处理。确保程序在运行时执行不依赖类型信息
虚表
在生成接口的引用时,需要构建胖指针,除了指向数据本身,还需要指向一个涵盖了这个接口所支持的方法的列表,这个列表就称为虚表 (virtual table)。虚表是编译时构建的,可以暂时理解为一张指向若干个函数地址的表,运行时可以通过这张表找出要执行的函数
虚表除了记录如何释放对象、对象的大小和对齐,还记录了数据能够执行的接口,所以在运行期可以根据上下文动态分派
运行方式
并发 (concurrency) 是一种能力,并行 (parallel) 是一种手段。并行是基于多核,可以同时处理多件事情。并发只是看起来可以同时处理多件事情,但是需要切换上下文。很多拥有高并发处理能力的编程语言,都会在用户程序里嵌入一个 M:N 的调度器来把 M 个并发任务合理分配到 N 个 CPU core 并行处理,达到最大程序吞吐量
同步是指一个任务开始执行之后,后续的操作会阻塞,知道任务结束。比如 CPU,只有流水线中前一条指令执行完成之后,才会执行下一条指令
同步执行保证了代码的因果关系,是程序正确性的保证
异步是指一个任务开始执行之后,与它没有因果关系的其他任务可以正常执行,不必等待前一个任务结束
async / await 是一个语法糖,它用状态机将 Promise 包装起来。async 定义了一个可以并发执行的任务,而 await 则触发这个任务并发执行
编程范式
泛型也称为参数化类型或者参数多态,支持泛型编程的语言,会提供强大的接口编程能力。泛型带来的好处可以延迟绑定,让数据结构的通用性更强,适用场景更广泛,大大减少了代码的重复,提高了可维护性