Skip to content

语言基础

基本语法

数据类型

操作符

操作符的本质是一些内置函数的语法糖,允许开发者使用简洁的符号调用一个全局可见的、常用的函数。

流程控制

if-else

while/for/loop

模式匹配 match

模式匹配是一种高级的 if-else 语句和 switch 的语法糖,它支持匹配一个变量的类型、结构,并提取和解构一个变量,相较于 if-else 更加方便,同时简化了丑陋的 switch 语句。

函数

面向数据 + 方法绑定

面向对象是一个火爆的特性,常见的语言几乎都或多或少地支持。Rust 中采取的是函数式编程语言中的思路,提供一种使用面向数据和方法绑定的机制来实现面向对象的封装,但是不支持继承。这种将数据和行为分离的书写方式,相较于直接在类里面定义数据和方法的写法,更加强调数据面和管理面的分离,同时也易于扩展某个数据类型的行为,同时默认将面向对象中的组合概念进行深刻地贯彻。

C 语言中不支持面向对象的特性,但是提供基础的面向数据和面向过程的能力,在 C 语言中,允许用户定义 struct 结构来封装一个复杂的纯数据,并且 C 的开发这会书写一系列以该结构体为第一个参数的函数,从而来模拟方法绑定的效果。

Go 语言中,也提供了类似 Rust 的面向数据 + 方法绑定的方式来管理一些复杂的数据类型

结构体

在 rust 中,使用 struct 关键字定义一个纯数据结构,同时可以使用 enum 关键字定义一类纯数据结构。struct 可以是字典结构体,也可以是一个元组结构体,

rust
struct MyDataStruct {
  name: String,
  age: u32
}

struct MyPointTuple(i32, i32, i32);

struct MyDataEmpty;

enum MyEnum {
  TypeEmpty,
  TypeTuple(i32, i32, i32),
  TypeStruct {
    name: String,
    age: u32
  },
}

impl 伴生结构体

trait(特性)vs interface(接口)

在 Rust 中,提供一种名叫 trait 的机制来实现类似面向对象中的 interface 的功能,

内置 trait

内置的 trait 作为语言的基础设施,广泛用于实现大家经常遇到的功能和需求,规定语言中的一些标准,甚至能够影响编译器的编译行为。

泛型

泛型是一个迫切求值类型函数,它可以通过泛型函数自动生成多种组合类型,其表现为一个数据类型的参数,通过 Type<T> 中的尖括号进行声明和调用。

数据类型包括简单类型和复杂类型,如 i32、f64,复杂类型使用 struct、enum、union 进行定义;

rust 具有强大的泛型系统,它支持类型修饰、泛型组合、关联类型等强大特性。

类型修饰

在数据类型的基础上对类型进行额外修饰,这些修饰包括:

  • 可变引用修饰与不可变引用修饰,&、&mut
  • 可变裸指针修饰和不可变裸指针修饰,*mut、*const
  • 生命周期修饰,'a、'static
  • 动态派发修饰,dyn trait_name

泛型组合 trait bound

关联类型

在 Haskell 等函数式的语言中,存在一种名叫高阶类型的特性,高阶类型对标的是高阶函数,普通的函数接受值然后输出值,高阶函数接受函数并且可以输出新函数,高阶类型接受高阶类型并可以输出新的高阶类型。

值空间类型空间
类型
二等函数泛型
一等函数+高阶函数一等类型+高阶类型
闭包?

二等函数和一等函数区别在于,一等函数可以被看做数据一样被传递,二等函数不能,因为二等函数是迫切求值的,它必须以调用的形式出现,而不能被当作值来使用和传递;闭包意味着能够动态生成一个函数,并且引用局部作用域中的变量。泛型意味着可以使用泛型生成一个新的类型,但是泛型本身不能作为类型来传递,因为泛型是迫切求值的。

在 rust 中,没有选择实现高阶类型,但是选择实现了关联类型,来在一定程度上解决泛型迫切求值的问题。

属性标记和宏

宏是生成代码的代码,特别是对于不能够进行动态解释的语言,为了简化编写代码的复杂性,使用宏可以加速我们的开发。

所有权和借用机制

所有权

在 rust 中,所有权是指一个保存在内存中的数据,需要有且仅有一个所有者。所谓所有者,本质就是一个指针,通常表现为一个变量名,或者是一个实例化对象的名字符号;

  • 每个值都有且只有一个所有者,但是一个值的所有者可以改变,也就是移动语义;
  • 当所有者离开作用域时,其拥有的值会被释放;
  • 赋值操作具有默认的移动语义,即旧变量赋值给新变量时,旧变量不再拥有该值;

借用机制意味着,一个值可以被指针 A 所有,但却可以被 B 借用,其核心规则是任意时刻,一个值只能有一个可变指针指向该值,或者同时有多个不可变指针指向该值,两种情况选择其一;

  • 一个可变指针
  • 多个不可变指针

移动和借用

移动,说明一个值的所有权发生了转移,原先的指针失效了,不能再通过原指针访问数据;借用,意味着新建了一个新的指针指向了该值,但是这个指针是一个引用,引用类型和原类型相差一个 & 符号,他们都属于指针,不带 & 是所有者,带 & 是借用者。

隐式(所有权)移动

在 rust 中,除了赋值符号 =,当对函数传入参数的时候、还有函数结束并返回值的时候、还有声明闭包函数的时候,同样也会发生所有权的移动,除非将函数的参数或者返回值声明为一个 & 符号的引用,这表明函数只是希望借用,本质上是一个指针。

智能指针

rust 中的默认指针,即引用,需要受到严格的借用检查的约束限制,在一些需要灵活的场景下无法满足业务需求,因此需要使用一些智能指针来管理内存,从而同时获得自由和安全,但是需要一定的开销

智能指针特性所有权共享线程安全性备注
Box<T> Deref堆分配递归结构、大对象
Rc<T> Deref共享所有权单线程共享数据
Arc<T> Deref共享所有权多线程共享数据
Weak<T> Deref防止循环引用适用于树结构
Cell<T> ?Deref轻量内部可变适用于 Copy 类型
RefCell<T> ?Deref内部可变性运行时可变借用
Mutex<T>内部可变性运行时可变借用
RwLock<T>内部可变性运行时可变借用
Atomic<T>原子操作运行时可变借用

作用域和生命周期

rust 中有块作用域、函数作用域和全局作用域,作用域会随着程序的执行而展开,同时随着程序的执行结束而关闭。一个变量往往在一个作用域中被声明,此时,我们称这个变量属于这个作用域,当这个作用域被关闭的时候,从属于该作用域的变量将会被清除,rust 将会自动调用这些变量的 drop 方法释放资源。

生命周期泛型

生命周期是对一个引用类型的描述,它表现为一个类型的泛型

unsafe

unsafe 是 rust 特性子集,通过使用 unsafe 关键可以将一个函数或者 trait 中方法的标记为 unsafe,从而开启 unsafe 特性:

  • 解引用裸指针
  • 解析 union 的字段

异步编程

在 IO 密集型的程序中,程序的瓶颈出现在程序执行无法避免的 IO 等待,这将导致程序因为进行阻塞 IO 而被操作系统挂起,从而使得程序不能充分占用 CPU,出现了资源利用不充分的情况。

模块系统和包管理

rust 的模块系统有三个主要抽象,package, crate, mod。理解模块是建构大型应用和复用社区生态的前提。