01 - 基本类型
1.1 - 机器类型
1.1.1 - 整数类型
- 无符号整数
类型 | 范围 |
---|---|
u8 | 0 到 2^8-1(0 ~ 255) |
u16 | 0 到 2^16-1(0~65535) |
u32 | 0 到 2^32-1 |
u64 | 0 到 2^64-1 |
u128 | 0 到 2^128-1 |
- 有符号整数
类型 | 范围 |
---|---|
i8 | -2^7 到 2^7-1(-128 ~ 127) |
i16 | -2^15 到 2^15-1(-32768 ~ 32767) |
i32 | -2^31 到 2^31-1 |
i64 | -2^63 到 2^63-1 |
i128 | -2^127 到 2^127-1 |
-
usize 和 isize 机器字
- usize 表示无符号,范围是 0 到 2^32-1,或者是 0 到 2^64-1。
- isize 表示有符号,范围是 - 2^31 到 2^31-1,或者是 - 2^63 到 2^63-1。
- 精度取决于机器的寻址空间大小,32 位机器是 32 位长,64 位机器是 64 位长。
- 数组索引必须是 usize 值。
- 数组或向量的大小,或者某些数据结构中的元素数量的值,也是 usize 值。
-
整数字面量
-
格式:进制标识 + 数值 + 类型
116i8 // 类型为i8,十进制数为116 0xcafeu32 // 类型为u32,十六进制数为cafe,十进制数为51966 0b0010_1010 // 类型自推断,二进制数为0010 1010,十进制数为42 0o106 // 类型自推断,八进制数为106,十进制数为70
-
长数值可以在数字之间插入下划线
- 十进制数每 3 未插入一个下划线:u32 最大数可以写为:4_294_967_295;
- 十六进制或二进制每 4 位插入一个下划线:0xffff_ffff;
- 数字与类型后缀可以用下划线分开:127_u8
-
-
字节字面量
- 用类字符字面量表示的 u8 值;
- 字节字面量智能出现在 ASCII 编码的字符。
-
需要转义的字符
- 对于难写或难读的字符,可以用十六进制写出它们的编码。如
b'\xHH'
字符 字符字面量 u8 数值 单引号(') b'\''
39u8 反斜杠(\) b'\\'
92u8 换行 b'\n'
10u8 回车 b'\r'
13u8 制表符 b'\t'
9u8 - 对于难写或难读的字符,可以用十六进制写出它们的编码。如
-
as
操作符(功能一)-
实现整数类型之间的转换。
assert_eq!(10_i8 as u16, 10_u16); assert_eq!(2525_u16 as i16, 2525_i16); assert_eq!(-1_i16 as i32, -1_i32); // 以符号填充 assert_eq!(65535_u16 as i32, 65535_i32); // 以零填充
-
截短:超出目的类型范围的转换会得到原始值对 2 的 N 次方取模后的值,其中 N 是目标类型的位宽。
assert_eq!(1000_i16 as u8, 232_u8); assert_eq!(65535_u32 as i16, -1_i16); assert_eq!(-1_i8 as u8, 255_u8); assert_eq!(255_u8 as i8, -1_i8);
-
1.1.2 - 浮点类型
-
浮点类型
类型 精度 范围 f32 IEEE 单精度(至少 6 位小数) 约 - 3.4×10^38 到 3.4×10^38 f64 IEEE 双精度(至少 15 位小数) 约 - 1.8×10^308 到 1.8×10^308 - 遵循 IEEE 754-2008 标准。
- 包含正、负无穷;
- 区分正、负零;
- 包含非数值(not-a-number)值
-
浮点字面量
整数部分 . 小数部分. e-4(指数部分) f64(类型后缀)
- 小数部分、指数和类型后缀三者至少要有一个存在
- 小数部分也可以只有 1 个小数点,如
5.
。 - 根据上下文推断,可以推断数值的类型。
- Rust 将整数字面量和浮点字面量看成不同的类型。
-
常量特殊值
- 由
std::f32
和std::f64
定义; - 正无穷:
INFINITY
; - 负无穷:
NEG_INFINITY
; - 非数值(Not A Number):
NAN
; - 最小值:
MIN
; - 最大值:
MAX
。
- 由
-
数学常量
- 由
std::f32::consts
和std::f64::consts
定义; - E、PI 等常用数学常量;
- 2 的平方根:
2f63.sqrt()
。
- 由
1.1.3 - 布尔类型
-
布尔类型 bool 的值:
true
false
==
、>
和<
等比较操作符都可以产生布尔值
-
if
和while
等控制结构,要求它们的条件必须是 bool 表达式if x {...}
不正确;- 必须写成
if x != 0 {...}
才可。
-
短逻辑操作符
&&
和||
同样要求它们的条件必须是 bool 表达式 -
as
操作符(功能二)-
可以把 bool 值转换为整数类型。
assert_eq!(false as i32, 0); // false转换为0 assert_eq!(true as i32, 1); // true转换为1
-
但不可以把整数类型转换为 bool 值。
-
-
Rust 在内存里使用一个字节作为 bool 值,因此可以创建一个指向它的指针。
1.1.4 - 字符类型
-
字符类型
char
:- 以 32 位值的形式,表示单个 Unicode 字符。
-
字符串类型
String
:- 字符串或文本流则使用 UTF-8 编码。
- 将文本表示为一个 UTF-8 的序列,而不是字符的数组。
-
字符字面量:
-
单引号引起来的字符,如
'8'
或'!'
。 -
需要转义的字符:
字符 字符字面量 单引号(') b'\''
反斜杠(\) b'\\'
换行 b'\n'
回车 b'\r'
制表符 b'\t'
-
-
可以在单引号里写出相应字符的十六进制 Unicode 码点:
码点范围 字符形式 U+0000
到U+007F
(ASCII 字符集)'\xFF'
,'*'
等于'\x2A'
任何 Unicode 字符 \uFFFFFF
,使用 1 至 6 位十六进制数0x0000
到0xD7FF
,或0xE000
到0x10FFFF
char
类型保存的码点范围 -
as 操作符(功能三):
-
把 char 类型转换为整数类型。
-
如果转换的整数类型小于 32 位,则字符值的高位(upper bits)会被截短。
assert_eq!('*' as i32, 42);
-
u8 是唯一可以转换为 char 类型的整数类型。
-
-
std::char::from_u32
可以将 u32 值转换为Option<char>
值- 有效会返回
Some(c)
,其中 c 就是转换后的 char 值。 - 无效则返回
None
。
- 有效会返回
1.2 - 元组
-
元组(tuple):
-
格式为:
("Brazil", 1985)
-
每个元素都可以是一个不同的类型。相反,数组的元素必须是相同的类型。
-
元组只允许用常量作为索引。数组索引是 usize 值。
-
可以使用元组来返回多个值:
fn split_at(&self, mid: usize) -> (&str, &srt); // 举例 let text = "I see the eigenvalue in thine eye"; let temp = text.split_at(21); let head = temp.0; let tail = temp.1; assert_eq!(head, "I see the eigenvalue "); assert_eq!(tail, "in thine eye");
-
-
基元类型
- 零元组
()
; - 没有返回值的函数的返回类型就是基元。如
std::mem::swap
- 零元组
-
允许在函数参数、数组、结构体、枚举等任何使用逗号的地方的末尾再加一个逗号。
(1,)
表示只有一个元素的元组,末尾的逗号是必须的
1.3 - 指针
Rust 使用指针时,必须显示地使用指针类型。
1.3.1 - 引用
- 可以在堆、栈上分配内存。
&x
借用了一个对x
的引用。*r
引用的则是r
指向的值。- 引用永远不会是空值。以下错误在编译时会被发现:
- 悬空指针
- 重复释放
- 指针失效
- 引用默认是不可修改的:
&T
:不可修改引用,类似 C 中的const T*
;&mut T
:可修改引用,类似 C 中的T*
。
1.3.2-Box
Box::new()
只可以在堆上分配内存。- 转移,处理分配到堆上的值。
1.3.3 - 原始指针
- 原始指针是不安全的指针类型,C/C++ 中的指针:
- *const T:不可修改指针。
- *mut T:可修改指针。
- 原始指针解引用,一般只能在 unsafe 块中进行。
1.4 - 序列
1.4.1 - 数组
-
类型
[T; N]
:- 表示一个
N
个值的数组,每个值的类型都是T
。 - 数组的大小是在编译时确定的常量,也是类型自身的一部分。
- 不能向数组中追加新元素,也不能缩短数组。
- 表示一个
-
支持索引:
v.len()
,返回序列v
的元素数量;v.[i]
,引用的是序列v
的第 i 个元素,i
必须是 usize 值;- 第一个元素是
v[0]
; - 最后一个元素是
v[v.len() - 1]
。
-
语法:
-
把一系列逗号分隔的值,用方括号括起来:
let lazy_caterer: [u32; 6] = [1, 2, 4, 7, 11, 16]; let taxonomy = ["Ainimalia", "Arthropoda", "Insecta"]; assert_eq!(lazy_caterer[3], 7); assert_eq!(taxonomy.len(), 3);
-
数组字面量
[V; N]
:V
是每个元素的值,N
是数组的长度。[0u8; 1024]
表示固定大小(1KB)的缓冲区,以 0 字节填充。 -
没有语法表示未初始化的数组。
-
-
可以直接在数组上调用切片的任何方法。
1.4.2 - 向量
-
类型
Vec<T>
,被称为T
类型的向量:- 一种动态分配、可扩展的类型
T
的值序列。 - 向量的元素保存在堆上,因此可以随意缩放。
- 支持追加新元素,追加其他向量,或者删除元素等操作。
- 一种动态分配、可扩展的类型
-
支持索引:
v.len()
,返回序列v
的元素数量;v.[i]
,引用的是序列v
的第 i 个元素,i
必须是 usize 值;- 第一个元素是
v[0]
; - 最后一个元素是
v[v.len() - 1]
。
-
创建向量:
-
vec!
宏:let mut v = vec![2, 3, 5, 7]; v.push(11); //可以动态向其中添加元素
-
可以模仿数组字面量的语法,讲一个值重复一定次数来构建向量:
fn new_pixel_buffer(rows: usize, cols: usize) -> Vec<u8> { vec![0; rowl * cols] // 没有用分号结尾,表示返回值 }
-
可以基于迭代器产生的值来构建向量:
let v: Vec<i32> = (0..5).collect(); assert_eq!(v, [0, 1, 2, 3, 4]);
-
Vec::new
创建向量【不推荐使用】:-
当缓冲区达到容量上限后,再给向量添加元素会导致一系列操作:
分配一个更大的缓冲区,将现有的内容复制过去,基于新缓冲区更新向量的指针和容量,最后释放旧缓冲区。
-
-
Vec::with_capacity
创建向量【推荐使用】:-
提前知道向量中需要保存的元素个数先创建一个足够大的缓冲区,存储所有元素的向量再逐个向向量中添加元素,从而避免内存的重新分配。
-
capacity
方法举例:let mut v = Vec::with_capacity(2); assert_eq!(v.len(), 0); // 向量的长度为0 assert_eq!(v.capacity(), 2); // 无需重新分配,即可包含的元素个数 v.push(1); v.push(2); assert_eq!(v.len(), 2); assert_eq!(v.capacity(), 2); v.push(3); assert_eq!(v.len(), 3); assert_eq!(v.capacity(), 4);
-
-
-
与数组一样,对向量也可以使用切片方法。
-
一个
Vec<T>
包含 3 个值:- 缓冲区的引用;
- 该缓冲区可以存储的元素个数(即容量);
- 实际存储的元素个数(即长度)。
-
可以在向量的任何位置,随意插入或删除元素,对于长向量来说,这些操作比较慢:
v.insert(3, 35)
:在索引 3 处插入 35;v.remove(1)
:删除索引 1 处的元素;v.pop()
:删除并返回最后一个元素,如果向量为空,则返回 None。
-
使用 for 循环可以迭代遍历向量:
let languages: Vec<String> = std::env::args().skip(1).collect(); for i in languages { println!("{}: {}", i, if i.len() % 2 == 0 { "functional" } else { imperative }); }
1.4.3 - 逐个元素地构建向量
- 以每次添加一个元素的方式,构建向量。
- 实际元素的个数,至少是缓冲区大小的一半。
- 对于较小的向量来说,
Vec::with_capacity
的实现速度比Vec::new
块。
1.4.4 - 切片(slice)
-
类型
&[T]
,被称为T
类型的共享切片;本质为对切片的引用。 -
类型
&mut [T]
,被称为T
类型的可修改切片; -
他们是对其他值(如数组或向量)中部分元素序列的引用:
- 切片等同于指针:包含切片中第一个元素的地址,和从这个元素起可以访问元素的数量。
&mut [T]
允许读写元素,但不能共享;&[T]
可以被多个读者读取,但不能修改元素。
-
支持索引:
v.len()
,返回序列v
的元素数量;v.[i]
,引用的是序列v
的第 i 个元素,i
必须是 usize 值;- 第一个元素是
v[0]
; - 最后一个元素是
v[v.len() - 1]
。
-
切片可以是任意长度:
- 不能直接保存到变量中;
- 也不能作为函数参数传递;
- 永远只能按引用传递。
-
切片的引用是一个胖指针(fat pointer),双字宽的值,保存如下内容:
- 指向切片第一个元素的指针
- 切片中元素的个数
-
普通引用与切片引用的不同:
-
普通引用是对单个值的非所有型指针;
-
切片引用是对多个值的非所有型指针。(切片应用适合操作一串同类数据的函数,这样的数据不管是存储在数组、向量,还是栈、堆上)
let v: Vec<f64> = vec![0.0, 0.707, 1.0, 0.707]; let a: fn print(n: &[f64]) { for elt in n { println!("{}", elt); } } print(&v); print(&a);
-
-
在索引中使用范围,可以获取对数组或向量切片的引用,或者对现有切片的切片的引用
print(&v[0.. 2]); // 打印v的前两个元素 print(&a[2..]); // 从a[2]开始打印a的元素 print(&sv[1.. 3]); // 打印v[1]和v[2]
1.5 - 字符串
1.5.1 - 字符串字面量
-
使用双引号引起来的字符串,就是字符串字面量。
-
在字符串字面量中,单引号无须转义,双引号需要转义。
-
使用技巧:
- 字符串可以分散到多行,换行符和空白符都包含在字符串中,同样会被输出。
- 如多行文本以反斜杠结尾,那么换行符、空白符都会被删除。
-
原始字符串
-
以小写字母
r
标记 -
其中的转义序列无效
let default_win_install_path = r"C:\Program Files\Gorillas"; let pattern = Regex:: new(r"\d+(\.\d+)*");
-
在原始字符串中包含双引号,那么需要在开头和末尾添加井字号标记:
println!(r###" This raw string started with 'r###"'. Therefore it does "###);
-
1.5.2 - 字节字符串
- 字节字符串使用前缀
b
标记的字符串字面量,是u8
值(字节)的切片 - 原始字节字符串使用前缀
br
标记 - 字节字符串不能包含任意 Unicode 字符,只能是 ASCII 和
\xFF
转义序列
1.5.3 - 字符串
-
字符串在内存中的表示
- 字符串在内存中使用
UTF-8
可变宽编码存储。 - 字符串中每个 ASCII 字符占用 1 个字节,其他字符占用多个字节。
String
字符串类型:在可伸缩的缓冲区上存储 UTF-8 文本,分配在堆上,可按需求或请求来调整大小。&str
字符串切片类型:是对其他 UTF-8 文本的引用,为胖指针(&[u8]
),包含实际数据的地址及其长度。"abcd"
字符串字面量:是一个引用预分配文本的&str
,与程序的机器码一期存储在只读内存中。
- 字符串在内存中使用
-
字符串
-
&str
等价于&[T]
;- 可以引用任何字符串的任意切片。
- 适合作为函数的参数。
-
String
等价于Vec<T>
; -
创建
String
的方法:-
.to_string()
方法:把&str
转换为String
。let error_message = "too many pets".to_string();
-
format!()
宏:返回一个新的String
。assert_eq!(format!("{}时{:02}分{}秒", 21, 5, 23), "21时05分23秒".to_string());
-
字符串数组、切片和向量的
.concat()
和.join(sep)
方法,可将多个字符串拼接成一个新的String
:let bits = vec!["veni", "vidi", "vici"]; assert_eq!(bits.concat(), "venividivici"); assert_eq!(bits.join(", "), "veni, vidi, vici");
-
-
-
使用字符串
-
字符串支持
==
和!=
操作符:当两个字符串包含的字符相同、顺序也相同,那么他们就是相等的。assert_eq!("ONE".to_lowercase() == "one");
-
字符串也支持比较操作符
<
、<=
、>
和>=
。
-
1.5.4 - 其他类似字符串的类型
- 对于 Unicode 文本,使用
String
和&str
。 - 处理文件名时,使用
std::path::PathBuf
和&Path
。 - 处理根本不是字符数据的二进制数据时,使用
Vec<u8>
和&[u8]
。 - 处理以操作系统原生形式表示的环境变量名和命令行参数时,使用
OsString
和&OsStr
。 - 与使用空字符结尾字符串的 C 库互操作时,使用
std::ffi::CString
和&CStr
。
详见《Rust 程序设计》(吉姆 - 布兰迪、贾森 - 奥伦多夫著,李松峰译)第三章
链接地址