变量与可变性
Rust 中, 变量 (Variable) 默认不可变:
let a = 0; // 定义 a 并绑定为 0
// a = 1; // 尝试为不可变变量 a 赋值 1, 报错如果需要可变性, 则必须手动添加可变 (Mutable) 关键字显式标记:
let a = 0;
let mut b = 1; // 定义可变的 b 并绑定值 1 给 b;
// a = 2; // <------- 不允许
b = 3; // <------- 允许不过注意, 不可变并非意味着不可初始化:
let a; // 定义 a
a = 1; // 绑定 a 为 1, 因为这是 a 第一次被赋值
// a = 2;// 再次尝试绑定, 报错如此分离定义引入与变量绑定, 并无问题, 但若需二次赋值, 则必须显式标记其可变.
类型推断
Rust 是静态强类型语言, 这意味着所有变量的类型都固定且不可变. 但上述代码中, 我们并没有手动写出变量的类型, 那么 Rust 该如何确定变量类型呢?
实际上, Rust 编译器装配了强大的类型推断能力, 只要编译器能够推断出变量的类型, 代码就允许编译通过. 上文代码等价于下述:
//----------------
let a: i32 = 0; // 根据初始化值直接推导
//----------------
let a: i32 = 0;
let mut b: i32 = 0;
b = 3;
//----------------
let a: i32;
a = 1; // 根据后文的初始化推导到前文但有些时候, 编译器无法成功推导类型, 此时必须手动标注类型. 一般只会出现在部分实现了复杂泛型的堆数据中. 遇到时就能明白为何不能自动推导.
默认类型
一些供于变量初始化的值有众多不同可行的类型, 此时如果不手动指定, 类型推导会使用其中的默认类型, 我们将在本文下面说明默认类型有哪些.
常量
可以用关键字 const 标记常量 (Constant):
const PI: f64 = 3.1415926;常量永远不具备可变性, 定义时也必须手动指定其数据类型. 此外, 常量允许定义在任何作用域中, 包括函数体外部:
const PI: f64 = 3.1415926;
fn main() {
println!("{}", PI);
const SHIMOKITAZAWA: i32 = 114514;
println!("{}", SHIMOKITAZAWA);
}此外, 常量仅允许常量表达式赋值, 也必须定义时就赋值:
const CONST_A: i32 = 114; // 直接赋值, 无错
const CONST_B: i32 = 114 + 514; // 常量表达式赋值, 无错, 将在编译期直接得到结果
//-----------------------------------------
// let tmp = 1919; // 先定义一个变量,
// const CONST_C: i32 = tmp + 810; // 然后定义常量为一个变量参与的表达式, 不允许
//------------------------------------------
// const CONST_D: i32; // 定义常量,
// CONST_D = 12345; // 然后赋值, 不可行, 报错.事实上, Rust 代码检查器设计上就不支持常量类型推导, 因为常量总会内联到使用处直接参与逻辑.
常量之于不可变变量
有人问不可变变量与常量的区别在何处. 不可变变量允许一次初始化赋值, 本次赋值允许 Rust 所有可行的表达式, 而常量仅仅作为某些固定值的助记符而存在, 不具有任何高级功能.
另一个角度, 为什么变量默认不可变? 答案是 Rust 由工程实践得出结论, 在现行语言中, 变量大多数情况下仅需要一次赋值, 而后无需可变, 但绝大多数变量仍可变, 这带来了不可预期的负面影响.
变量遮蔽
Rust 中多次定义相同名称的变量完全可行:
fn main() { //<----进入主函数域
let a = 1; // 定义 a1: i32 为 1
let a = 2; // 遮蔽 a1, 定义 a2: i32 为 2
let a = "3"; // 遮蔽 a2, 定义 a3: &str 为字符串字面量 "3"
let a = String::from(a);// 遮蔽 a3, 定义 a4 为 a3 被 String 库的 from 方法转换得到的字符串 "3"
println!("{a}");
{ //<----进入域
println!("{a}");
let a = 5.555; // 遮蔽 a4, 定义 a5: f64 为 5.555
println!("{a}");
let a = "6"; // 遮蔽 a5, 定义 a6: &str 为字面量 "6"
} //<----退出域, a6 被释放, 导致 a5 重新可见
// a5 同样因退出域被释放, 导致 a4 重新可见
println!("{a}");
} //<----退出域, a4 被释放.
// a3, a2, a1 依次取消遮蔽, 但同时因为离开作用域, 被释放编译运行上述代码, 理应得到结果:
"3"
"3"
5.555
"3"根据我们将会学到的生命周期系统, 可以这么理解:
fn main() { //-----------------------------------//
let a = 1; // a1 //
let a = 2; // | a2 //
let a = "3"; // | | a3 //
let a = String::from(a); // | | | a4 //
println!("{a}"); // | | | |<--------------//----- println!("{a}");
{ //-----|--|--|--|---------// //
println!("{a}"); // // | | | |<--------//----//----- println!("{a}");
let a = 5.555; // // | | | | a5 // //
println!("{a}"); // // | | | | |<-----//----//----- println!("{a}");
let a = "6"; // // | | | | | a6 // //
} //-----|--|--|--|--x-- x--// //
println!("{a}"); // | | | |<--------------//----- println!("{a}");
} //----------x--x--x--x---------------//后定义的变量将覆盖在先定义的变量上, 当方法或函数希望调用变量时, 仅会访问到最上层的那个.
数据类型
基本的数据类型分两类, 标量类型与复合类型. 前者直接表示量, 后者表示量的组合.
标量类型
整形 (Integer)
顾名思义, 表示整数.
整形按占据比特数多少与是否有符号有如下类型:
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8 | i8 | u8 |
| 16 | i16 | u16 |
| 32 | i32 | u32 |
| 64 | i64 | u64 |
| 128 | i128 | u128 |
| arch | isize | usize |
其中 isize 和 usize 占据比特数与操作系统位数 (32/64) 一致.
以 i8 类型为例, 其在内存中以如下形式存储:
可见 i8 类型能表示的最小数即为 , 最大数即为 .
补码
关于负数的表示牵涉到补码问题, 不甚重要, 可以自行了解.
字节类型能够表示的数, 总计必然为 个, 对 而言, 范围为 , 而对 而言, 范围为 .
除了表示范围及有无符号外, 整形的字面形式也有多种, 不止于我们常见的十进制:
| 字面值 | 例子 | 写法 |
|---|---|---|
| Decimal | 114_514 | 直接写 |
| Hexadecimal | 0x1234abcd | 0x 开头 |
| Octal | 0o123456 | 0o 开头 |
| Binary | 0b1101_1011 | 0b 开头 |
| Byte (u8) | b'A' | 以 b' ' 包裹的单 ASCII 字符 |
下划线
数字中的任意下划线均会被忽略, 仅用于视觉效果. 1_2_3_4 与 1234 完全同义.
ASCII
一种通用规范, 定义了从 到一些符号的映射表, 有时很好用. 见文末附表.
浮点型 (Float)
浮点是实型变量的内部实现方式, 其他实现方式还有定点型, 几无使用, 不必赘述.
机制
浮点数的机制是总长度固定作为尾数, 小数点根据指数位所描述的浮动, 外带一个符号位, 以 f32 为例大致如下, 采用 IEEE 754 标准, 32 位内存:
举例, f32 数字 即表示
.
Rust 官方提供了下面几种浮点型, 均具符号位:
| 长度 | 类型 |
|---|---|
| 32 | f32 |
| 64 | f64 |
目前关于 16 位及 128 位浮点数, 官方社区仍在讨论.
Bool 类型
let some_boolean: bool = true;该类型变量有且仅允许有两个可能的值: true 和 false. 分别对应逻辑真与逻辑假.
概念上看, Bool 类型仅需 个比特位即可存储, 但实际上该类型数据需要占据整整 . 这出于内存寻址机制、健壮性、C/C++ 接口兼容性考虑.
位图
就算采用后文的向量/数组容器 Vec<T>, Vec<bool> 的每一个分量作为 bool 类型仍然占据 字节. 此时考虑第三方库 bitvec 所提供的容器, 在底层会将布尔值打包为比特位, 在一些权衡情境下更具优势.
后文介绍的控制语句 if ... {..}, 就必须以一个 Bool 类型变量为控制判据.
字符类型
let some_char: char = 'A';字符类型允许表示一个 Unicode 标量值, 占据 4 字节. 为什么是 4 字节呢? Rust 要求所有这些标量类型均为定长, 权衡存储效率与性能, 采用了 UTF-32 编码表示字符类型, 恰为 4 字节.
进一步
标准的 UTF-32 编码允许无效代理项 ( U+D800 ~ U+DFFF ) 码点, 但 Rust 中仅允许 Unicode 标量值范围的 UTF-32 ( U+0000 ~ U+D7FFF, U+E000 ~ U+10FFFF ).
注意到两个十六进制数对应一个字节.
复合类型
该类类型允许标量类型间组合.
Tuple 元组
let a = ('A', 1, 3.14);
let b: (i32, f64, u8) = (100, 2.3, 2);
let c = b.0;如上, 用圆括号和逗号将多个数据括起来, 如 a, 当希望指定类型时书写如 b. 显而易见, 元组允许任意不同类型组合. 如需单独获取某一分量, 用句点和索引指定, 如 c.
组成元组的每个元素必须定长. 这句话并不仅仅是限制, 我们可以定义这种元组:
let a = ('1', (2, 3.0, 4), false, (5.5));需要注意到单元素元组与元素本身等价.
Array 组
let arr1 = [1,2,3];
let arr2 = [i32; 3]// = [1,2,3];
let arr3 = [3;5];
let a = arr1[0];显然与元组的区别: 用中括号标记, 定长, 类型唯一.
由于类型唯一, 允许语法糖定义足够长的组, 如 arr2 和 arr3.
此外, 如果需要获取组的某一分量, 按 a 的方式用中括号标明偏移值.
偏移值和索引
可以直接以 C 语言为例:
数组的偏移值, 指的是数组头指针的偏移值:
int a[]= {1,2,3};如此定义一个数组, 实际上是定义一个 int 指针, 我们可以用指针访问数组元素:
*a; // == *(a+0) == a[0] == 1
*(a+1); // == *(1+a) == a[1] == 1[a] == 2换言之, C 语言中的数组只是指针的语法糖. 在此定义下, 自然有必要让数组索引与指针偏移值对齐, 换言之, 以 0 开始而非 1.
绝大多数编程语言都继承了这个习惯, 其中也包括 Rust. 不过一些专精科研计算的语言如 MATLAB 和 R 语言就选择以 1 为索引起始.
模式匹配:
模式与模式匹配是 Rust 中极强大的工具, 此处简要介绍其与元组、组相关的简单语法.
let arr = ("mp4","m4v","mov");
let (mp4, m4v, mov) = arr;
//---------变量遮蔽------------
let arr = ["mp4","m4v","mov"];
let [mp4, m4v, mov] = arr;
// 也可以只取用所需
let [mp4,..] = arr;
let [_,m4v,_] = arr;如上, 可以通过构建相同形状的 "左值" 去绑定复合类型的右值, 将复合类型的内容提取出来. 此外可以使用通配符 _ 表示匹配元素但不绑定到任何变量, 使用 .. 占位符表示忽略剩余部分.
附表: ASCII 编码表
| 二进制 | 十进制 | 十六进制 | 缩写 | 插入记号表示法 | 释义 |
|---|---|---|---|---|---|
| 0000 0000 | 0 | 00 | NUL | ^@ | 空字符 Null |
| 0000 0001 | 1 | 01 | SOH | ^A | 标题开始 |
| 0000 0010 | 2 | 02 | STX | ^B | 正文开始 |
| 0000 0011 | 3 | 03 | ETX | ^C | 正文结束 |
| 0000 0100 | 4 | 04 | EOT | ^D | 传输结束 |
| 0000 0101 | 5 | 05 | ENQ | ^E | 询问字符 |
| 0000 0110 | 6 | 06 | ACK | ^F | 确认回应 |
| 0000 0111 | 7 | 07 | BEL | ^G | 响铃字符 |
| 0000 1000 | 8 | 08 | BS | ^H | 退格 |
| 0000 1001 | 9 | 09 | HT | ^I | 水平制表符 |
| 0000 1010 | 10 | 0A | LF | ^J | 换行 |
| 0000 1011 | 11 | 0B | VT | ^K | 垂直制表符 |
| 0000 1100 | 12 | 0C | FF | ^L | 换页 |
| 0000 1101 | 13 | 0D | CR | ^M | 回车 |
| 0000 1110 | 14 | 0E | SO | ^N | 移出/取消变换 |
| 0000 1111 | 15 | 0F | SI | ^O | 移入/激活变换 |
| 0001 0000 | 16 | 10 | DLE | ^P | 数据链路转义 |
| 0001 0001 | 17 | 11 | DC1 | ^Q | 设备控制1 |
| 0001 0010 | 18 | 12 | DC2 | ^R | 设备控制2 |
| 0001 0011 | 19 | 13 | DC3 | ^S | 设备控制3 |
| 0001 0100 | 20 | 14 | DC4 | ^T | 设备控制4 |
| 0001 0101 | 21 | 15 | NAK | ^U | 确认失败回应 |
| 0001 0110 | 22 | 16 | SYN | ^V | 同步空闲 |
| 0001 0111 | 23 | 17 | ETB | ^W | 传输块结束 |
| 0001 1000 | 24 | 18 | CAN | ^X | 取消 |
| 0001 1001 | 25 | 19 | EM | ^Y | 介质结束 |
| 0001 1010 | 26 | 1A | SUB | ^Z | 替换 |
| 0001 1011 | 27 | 1B | ESC | ^[ | Escape |
| 0001 1100 | 28 | 1C | FS | ^\ | 文件分隔符 |
| 0001 1101 | 29 | 1D | GS | ^] | 组分隔符 |
| 0001 1110 | 30 | 1E | RS | ^^ | 记录分隔符 |
| 0001 1111 | 31 | 1F | US | ^_ | 单元分隔符 |
| 0010 0000 | 32 | 20 | - | - | 空格 |
| 0010 0001 | 33 | 21 | - | - | ! |
| 0010 0010 | 34 | 22 | - | - | " |
| 0010 0011 | 35 | 23 | - | - | # |
| 0010 0100 | 36 | 24 | - | - | $ |
| 0010 0101 | 37 | 25 | - | - | % |
| 0010 0110 | 38 | 26 | - | - | & |
| 0010 0111 | 39 | 27 | - | - | ' |
| 0010 1000 | 40 | 28 | - | - | ( |
| 0010 1001 | 41 | 29 | - | - | ) |
| 0010 1010 | 42 | 2A | - | - | * |
| 0010 1011 | 43 | 2B | - | - | + |
| 0010 1100 | 44 | 2C | - | - | , |
| 0010 1101 | 45 | 2D | - | - | - |
| 0010 1110 | 46 | 2E | - | - | . |
| 0010 1111 | 47 | 2F | - | - | / |
| 0011 0000 | 48 | 30 | - | - | 0 |
| 0011 0001 | 49 | 31 | - | - | 1 |
| 0011 0010 | 50 | 32 | - | - | 2 |
| 0011 0011 | 51 | 33 | - | - | 3 |
| 0011 0100 | 52 | 34 | - | - | 4 |
| 0011 0101 | 53 | 35 | - | - | 5 |
| 0011 0110 | 54 | 36 | - | - | 6 |
| 0011 0111 | 55 | 37 | - | - | 7 |
| 0011 1000 | 56 | 38 | - | - | 8 |
| 0011 1001 | 57 | 39 | - | - | 9 |
| 0011 1010 | 58 | 3A | - | - | : |
| 0011 1011 | 59 | 3B | - | - | ; |
| 0011 1100 | 60 | 3C | - | - | < |
| 0011 1101 | 61 | 3D | - | - | = |
| 0011 1110 | 62 | 3E | - | - | > |
| 0011 1111 | 63 | 3F | - | - | ? |
| 0100 0000 | 64 | 40 | - | - | @ |
| 0100 0001 | 65 | 41 | - | - | A |
| 0100 0010 | 66 | 42 | - | - | B |
| 0100 0011 | 67 | 43 | - | - | C |
| 0100 0100 | 68 | 44 | - | - | D |
| 0100 0101 | 69 | 45 | - | - | E |
| 0100 0110 | 70 | 46 | - | - | F |
| 0100 0111 | 71 | 47 | - | - | G |
| 0100 1000 | 72 | 48 | - | - | H |
| 0100 1001 | 73 | 49 | - | - | I |
| 0100 1010 | 74 | 4A | - | - | J |
| 0100 1011 | 75 | 4B | - | - | K |
| 0100 1100 | 76 | 4C | - | - | L |
| 0100 1101 | 77 | 4D | - | - | M |
| 0100 1110 | 78 | 4E | - | - | N |
| 0100 1111 | 79 | 4F | - | - | O |
| 0101 0000 | 80 | 50 | - | - | P |
| 0101 0001 | 81 | 51 | - | - | Q |
| 0101 0010 | 82 | 52 | - | - | R |
| 0101 0011 | 83 | 53 | - | - | S |
| 0101 0100 | 84 | 54 | - | - | T |
| 0101 0101 | 85 | 55 | - | - | U |
| 0101 0110 | 86 | 56 | - | - | V |
| 0101 0111 | 87 | 57 | - | - | W |
| 0101 1000 | 88 | 58 | - | - | X |
| 0101 1001 | 89 | 59 | - | - | Y |
| 0101 1010 | 90 | 5A | - | - | Z |
| 0101 1011 | 91 | 5B | - | - | [ |
| 0101 1100 | 92 | 5C | - | - | \ |
| 0101 1101 | 93 | 5D | - | - | ] |
| 0101 1110 | 94 | 5E | - | - | ^ |
| 0101 1111 | 95 | 5F | - | - | _ |
| 0110 0000 | 96 | 60 | - | - | ` |
| 0110 0001 | 97 | 61 | - | - | a |
| 0110 0010 | 98 | 62 | - | - | b |
| 0110 0011 | 99 | 63 | - | - | c |
| 0110 0100 | 100 | 64 | - | - | d |
| 0110 0101 | 101 | 65 | - | - | e |
| 0110 0110 | 102 | 66 | - | - | f |
| 0110 0111 | 103 | 67 | - | - | g |
| 0110 1000 | 104 | 68 | - | - | h |
| 0110 1001 | 105 | 69 | - | - | i |
| 0110 1010 | 106 | 6A | - | - | j |
| 0110 1011 | 107 | 6B | - | - | k |
| 0110 1100 | 108 | 6C | - | - | l |
| 0110 1101 | 109 | 6D | - | - | m |
| 0110 1110 | 110 | 6E | - | - | n |
| 0110 1111 | 111 | 6F | - | - | o |
| 0111 0000 | 112 | 70 | - | - | p |
| 0111 0001 | 113 | 71 | - | - | q |
| 0111 0010 | 114 | 72 | - | - | r |
| 0111 0011 | 115 | 73 | - | - | s |
| 0111 0100 | 116 | 74 | - | - | t |
| 0111 0101 | 117 | 75 | - | - | u |
| 0111 0110 | 118 | 76 | - | - | v |
| 0111 0111 | 119 | 77 | - | - | w |
| 0111 1000 | 120 | 78 | - | - | x |
| 0111 1001 | 121 | 79 | - | - | y |
| 0111 1010 | 122 | 7A | - | - | z |
| 0111 1011 | 123 | 7B | - | - | { |
| 0111 1100 | 124 | 7C | - | - | | |
| 0111 1101 | 125 | 7D | - | - | } |
| 0111 1110 | 126 | 7E | - | - | ~ |
| 0111 1111 | 127 | 7F | DEL | ^? | Delete 字符 |