Rust 基础
参考资料
安装环境
系统程序员也能享受美好
- 吹一堆 nb
- Rust 不仅是实现了零成本的垃圾回收, 同时包括零成本的并行内存安全
我的观点
- 任何一种编程语言, 只要是图灵完备的, 就能描述世界上所有的代码
- Rust 追求的理念, 无非就是将代码限制于一个较小的子集
- 恰好能够保证通过静态分析的方法就能实现内存安全的同时, 能够拥有足够强的灵活性与表达力
- 除此之外, 他还融入了大量已经广泛使用的零成本抽象
Rust 导览
- Rust 确实拥有非常优秀的一个工具链
- cargo 提供了一些规范性的项目结构, 这往往我们认为是框架当中应该提供的
- 以及非常优秀的包管理, 非常优秀的构建, 在一定程度上可以代替 make
- 一个 Rust 包, 称为一个 crate
[package]
name = "hello_world"
version = "0.1.0"
edition = "2018"
[dependencies]
... = "1.0.0"
函数
fn add(mut a:i32, b:i32) -> i32 {
println!("Hello, world!");
}
mut
表示可变参数, 默认为不可变
assert!()
表示断言, 用于调试, 其中, !
表示为宏调用
{
...;
m // 任意以 {} 包住的代码块都可以用作表达式,
// 若以没有尾随着分号的表达式结尾, 则这个表达式的值就是整个表达式的值
// 如果没有这个结尾无分号表达式的话, 则这个表达式的值就是 "()"
}
- Rust 存在
return
, 适用于在函数中提前返回
属性
#[test] // debug 时运行, 发布时忽略
fn test_add() -> () {
assert_eq!(add(1, 2), 3);
}
特型
- 类似接口, 任意类实现了这个特型, 就可以使用这个特型的方法
- 使用
use
关键字引入特型与模块
Result
与 Option
- 两个枚举类型
Result
表示成功或失败, 成功则返回 Ok(T)
, 失败则返回 Err(E)
Option
表示可选值, 有值则返回 Some(T)
, 无值则返回 None
基本数据类型
- Rust 拥有简单的类型推断和泛型函数, 这种泛型也是零成本抽象
- Rust 几乎不会进行隐式类型转换, 可以使用
num as type
去进行显示转换
固定宽度的数值类型
- u8-u128, i8-i128, f32-f64, 显而易见, 非常爽
- usize 与 isize 表示与当前机器字等宽的类型
- 在字面值当中可以指定类型, 如果没有指定, 会进行类型推断, 有多种候选类型, 默认使用 i32, 无法认定则报错
- 0x, 0o, 0b 作为进制前缀
操作
(-4).abs()
是错误的, 因为无法进行任何类型推断
-4i32.abs()
可能与你的预期不符, 方法的优先级大于一元运算符
(-4i32).abs()
与 i32::abs(-4)
可能达到你的预期
溢出
- 当你进行调试构建的时候, 整数运算溢出会产生一个 panic 或者说 'error'
- 但在发布构建当中, 会发生回绕, 也就是说正确的结果对类型范围的取模
- 整型提供方法去禁止这些行为
checked_
方法, 结果正确返回 Some(result)
, 结果不正确则为 None
wrapping_
方法, 结果正确返回 result
, 结果不正确则回绕
saturating_
方法, 结果正确返回 result
, 结果不正确则返回边界值
overflowing_
方法, 结果正确返回 (result, false)
, 结果不正确则返回 (result, true)
- 这些前缀支持以下几种运算后缀
add
, sub
, mul
, div
, rem
, shl
, shr
neg
(取反), abs
, pow
浮点类型
- f32/f63 精度至少为 6/15 位
- 浮点数的字面量类型行为与整型差不多, 默认类型是 f64
- 特别的, 字面量包含
MIN
, MAX
, NEG_INFINITY
, INFINITY
, NAN
- 浮点数提供一些方法, 比如
sqrt
std::f32::consts
模块提供了一些常量, 比如 PI
, E
布尔类型
字符类型
- 字符类型与整型截然不同
- 字符类型是
char
, 占 4 字节, 可以表示任何 Unicode 字符
- 转义字符是有效的, 如
'\n'
, '\xHH'
(HH 是任意两位十六进制数)
- 特别的, 可以使用
b'c'
去表示一个 u8 字面量 (仅限 ASCII 码)
- u8 类型可以使用
as
转换为 char
- char 类型可以使用
as
转换为 整型 (可能截断)
std::char::from_u32
方法可以将 u32 转换为 char
- 其返回值是
Option<char>
(Some(char)
或者 None
)
string
是 UTF-8 字节序列而非 char 数组
元组类型
- 元组类型类似与结构体, 其类型表达为
(T1, T2, ..., Tn)
- 元组类型可以使用
let
进行解构, 如 let (x, y) = (1, 2)
- 对于一个元组对象
name.0
可以访问其第 0 个元素, 以此类推
()
表示一个空元组, 其类型为 ()
, 类似于 void
指针类型
- Rust 旨在将内存分配保持在最低限度, 如
((10, 20), (30, 40))
等效于 (10, 20, 30, 40)
- Rust 提供了
&
引用类型, 其行为类似指针, 称 &x
是借用了对 x 的引用
- Rust 有两种引用, 它们是互斥的, 只能二选一, 分别是共用引用和可变引用
&x
是共用引用, 可以被多个引用同时拥有
&mut x
是可变引用, 只能有一个引用拥有
let a = Box::new(10);
可以创建一个堆上的对象, 其类型为 Box<T>
Box<T>
是一个智能指针, 其行为类似指针, 但其可以自动释放
- 除非
Box<T>
被移动 (move)
- 对于裸指针
*const T
和 *mut T
, 解引用时需要使用 unsafe
数组, 向量和切片
- 数组是固定长度的序列, 其类型为
[T; n]
, 其中 n
是一个常量表达式
- 向量是可变长度的序列, 其类型为
Vec<T>
, 其元素存在于堆上
- 切片是对一系列值的引用, 其类型当然也包括
&mut [T]
与 &[T]
- 切片从数组或向量的某一点开始, 一直到它的结束
let a = [1, 2, 3, 4, 5]; // 初始化一个数组
let b = [0u8; 10]; // 初始化一个长度为 10 的数组, 所有元素为 0
for i in 0..a.len() { // 遍历数组
a[i] = i as u8;
}
if a[0] == 0 { // 访问数组元素
a[0] = 1;
}
```rust
let mut v = Vec::new(); // 初始化一个向量
let mut v = vec![1, 2, 3]; // 初始化一个向量, 并添加元素
let mut v = vec![0u8; 10]; // 初始化一个长度为 10 的向量, 所有元素为 0
let v: Vec<u8> = (0..10).collect(); // 初始化一个向量, collect() 表示将元组迭代器的所有元素收集到一个向量中
let mut v = Vec::with_capacity(10); // 初始化一个容量为 10 的向量
v.push(1); // 向向量中添加元素
v.pop(); // 弹出向量的最后一个元素
v.len(); // 获取向量的长度
v.capacity(); // 获取向量的容量
v.is_empty(); // 判断向量是否为空
v.clear(); // 清空向量
v.insert(0, 1); // 插入元素到向量的第 0 个位置
v.remove(0); // 移除向量的第 0 个元素
- 向量本质包含一个指针, 一个长度, 一个容量
- 当向量的长度超过容量时, 向量会销毁缓冲区, 并重新分配一个更大的缓冲区
- Vec 不是内置类型
字符串
- Rust 提供了两种字符串类型, 分别是
String
和 &str
let s = "Hello, world!"; // 初始化一个字符串
let s = "Hello, w \ // 注意先导空格
orld!" // 等效
let s = "Hello,
world!" // 会保留换行符
let s = r#"..."# // 原始字符串, 不会转义
// 任意数量的 # 标识边界, 以在原始字符串当中使用 " 和 #
// 字节串
let s = b"Hello, world!"; // &[u8; n] 类型
- 内存中使用 UTF-8 编码表示字符串, 其单字符的长度可以是 1 到 4 字节
- String 类型类似
Vec<u8>
, &str 类型类似 &[u8]
- &str 对于预分配文本行为类似 C 中的字符串字面量
let s = "Hello, world!".to_string(); // 将字符串字面量转换为 String
let s = format!("Hello, {}!", "world"); // 将字符串字面量转换为 String
// ss 为字符串的数组, 向量, 切片
let s = ss.join(", "); // 将字符串数组, 向量, 切片连接成一个字符串
let s = ss.concat(); // 将字符串数组, 向量, 切片连接成一个字符串
- 字符串可以使用
==
, !=
, <
, <=
, >
, >=
进行比较
- 但其行为因为 Unicode 的性质并不总符合你的预期
- 特别的, Rust 为 C 字符串 / OsString / 文件名提供了一些专用的类型
类型别名
type name = Vec<u8>
可以定义类型别名
所有权与移动