之所以世界上有很多类型的书, 是因为世界上有很多类型的人, 而每个人都在追求不同类型的知识. --Lemony Snicket
核心思想
Rust会尽可能去满足类型的要求, 但前提是程序足够可靠 (否则将对可能对类型提出要求)
两个类型特性:
- 类型推断: 根据给定的变量或者表达式, Rust允许省略类型
- 函数泛型: 单个函数可以处理不同类型的值
常见类型表
常见特性:
- char ≠ u8
- u8需要使用字节字面量:
b'X'
- 其他的类型 (比如: 任意精度整数) 可以使用 num crate
- Rust要求数组索引为usize (用于表示元素数量或者长度通常也使用usize)
- 字面量类型确定:
- 显式后缀确定: 43u8
- 隐式延迟确定: 无后缀字面量, 找到一处可以确定类型的代码 (如果无法确定, 默认使用i32和f64)
- 下划线分割: 4_323_333_215
类型 | 名称 | 实例 | 链接 |
---|---|---|---|
i8, i16, i32, i64, i128 | 给定位宽的有符号整数 | 42, -18, 0x400i16, 20_922_789_77 | |
u8, u16, u32, u64, u128 | 给定位宽的无符号整数 | 同上 | |
isize, usize | 与机器字等长的有/无符号整数 | ||
f32, f64 | 浮点数 | ||
bool | 布尔值 | true, false | |
char | Unicode字符 (4字节) | ||
(char, u8, i32) | 元组 | ||
() | "单元" (空源组) | ||
struct S { x: f32, y: f32 } | 具名字段型结构体 (字段具有名称x和y) | [(Rust程序设计) Chapter9-结构体]((Rust程序设计) Chapter9-结构体 "wikilink") | |
struct T(i32, char) | 元组型结构体 (字段不具有名称) | ||
struct E; | 单元型结构体 (无字段) | ||
enum Attend { OnTime, Late(u32) } | 枚举 | [(Rust程序设计) Chapter10-枚举和模式]((Rust程序设计) Chapter10-枚举和模式 "wikilink") | |
Box<Attend> | Box: 指向堆中值的具有型指针 | Box::new(Late(15)) | |
&i32, &mut i32 | 共享引用 和 可变引用 非拥有型指针, 其生命周期不能超出引用目标 | ||
String | UTF-8字符串, 动态分配大小 | ||
&str | 对str的引用: 指向UTF-8文本的非拥有型指针 | ||
[f64; 4] [u8; 256] | 数组, 固定长度, 元素类型相同 | ||
Vec<f64> | 向量, 可变长度, 元素类型相同 | ||
&u8, *mut u8 | 对切片(数组或向量的某部分)的引用, 包含指针和长度 | ||
Option<&str> | 可选值: 1. None: 无值 2. Some(v): 有值, 值为v | ||
Result<u64, Error> | 可能失败的操作结果: 1. Ok(v): 成功值, v为值 2. Err(e): 失败值, e为失败号 | ||
&dyn Any, &mut dyn Read | 特型对象, 是对任何实现了一组给定方法的值的引用 | [(Rust程序设计) Chapter11-特型和泛型]((Rust程序设计) Chapter11-特型和泛型 "wikilink") | |
fn(&str) -> bool | 函数指针 | ||
闭包无显式书写形式 | 闭包 | |a, b| a * a + b * b |
类型特性
类型解析
三种情况:
- 通过上下文可以确定唯一类型: 确定类型
- 通过上下文存在多义性: 默认使用i32
- 无上下文可以判断类型: 调用特定类型的方法时, 编译错误
println!("{}", (-4).abs()); // 编译错误, 原因: 无上下文确定-4的类型, 直接调用方法导致编译错误
println!("{}", i32::abs(-4)); // 编译成功, 原因: 外部函数调用的运算优先级高于-4, 构成了字面值的上下文, rust可以通过该函数解析得到-4的类型为i32
类型转换
三种情况:
- 范围内类型转换: 遵循相同的位模式
- 有符号扩展: 符号填充和零填充
- 有无符号类型转换: 相同的位模式
fn main() {
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); // 填零扩展 (虽然最高位为0)
// 超出范围: 截断
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);
}
运算符优先级
#Rust-point
重点:
- 方法调用 > 一元运算符
-4_i32.abs()
相当于-(4_i32.abs()) == -4
运算符/表达式 | 结合性 |
---|---|
Paths (路径) | |
Method calls (方法调用) | |
Field expressions (字段表达式) | → |
Function calls, array indexing (函数调用, 数组索引) | |
? | |
Unary (一元运算符): - , * , ! , & , &mut | → |
as | |
* , / , % | → |
+ , - | → |
<< , >> | → |
& | → |
^ | → |
| | → |
==, !=, < , > , <= , >= | 需要圆括号 |
&& | → |
|| | → |
.. , .= | 需要圆括号 |
=, +=, -=, =, /=, %= &=, |=, ^=, <<=, >>= | ← |
return break closures (返回、中断、闭包) |
3.1 固定宽度数据类型
检查算法 & 回绕算法 & 饱和算法 & 溢出算法
Rust处理整形运算溢出的两种情况:
- 调试构建: 引起panic
- 发布构建: 回绕算法
名称 | 前缀 | 说明 | 实例 |
---|---|---|---|
检查运算 | checked | checked运算将返回结果的Option值, 数学意义上正确则放回Some(v), 否则返回 | assert_eq!(10_u8.checked_add(20), Some(30)); assert_eq!(100_u8.checked_add(200), None); assert_eq!((-128_i8).checked_div(-1), None); |
回绕运算 | wrapping | 返回"数学意义上正确的结果"对"值域"取模的结果 | assert_eq!(100_u16.wrapping_mul(200), 20000); assert_eq!(500_u16.wrapping_mul(500), 53392); assert_eq!(500_i16.wrapping_mul(500), -12144); |
饱和运算 | saturating | 返回最接近"数学意义上正确的结果"的值 1. 不存在饱和除法: 整数相除一般不会溢出, 即使溢出也没有"数学意义上正确的结果" 2. 不存在饱和求余: 求余运算不会溢出 3. 不存在饱和位移法: 移位溢出存在多种补救方式, 无法统一支持 | assert_eq!(32760_i16.saturating_add(10), 32767); assert_eq!((-32760_i16).saturating_sub(10), -32768); |
溢出运算 | overflowing | 返回一个元组(result, overflowed) result: 回绕值 overflowed: bool值, true表示溢出, false表示未溢出 | assert_eq!(5_u16.overflowing_shl(17), (10, true)); |
特殊运算名称
运算 | 名称后缀 | 例子 |
---|---|---|
加法 | add | 100_i8.checked_add(27) = Some(127) |
减法 | sub | 128_u8.saturating_mul(3) == 255 |
乘法 | mul | |
除法 | div | |
求余 | rem | |
取负 | neg | |
绝对值 | abs | |
求幂 | pow | |
按位左移 | shl | |
按位右移 | shr |
u8
转义字符 (u8)
两种格式:
* 字符形式: b'x'
* 数值形式: b'\x1b'
转义字符 | 描述 | 示例代码 | 输出 |
---|---|---|---|
\n | 换行符 | let c: u8 = b'\n'; | 换行 |
\r | 回车符 | let c: u8 = b'\r'; | 回车 |
\t | 水平制表符 | let c: u8 = b'\t'; | 制表符 |
\0 | 空字符(空字节) | let c: u8 = b'\0'; | 空字符 |
\\ | 反斜杠 | let c: u8 = b'\\'; | \ |
\' | 单引号 | let c: u8 = b'\''; | ' |
\" | 双引号 | (不适用,u8 字节字面量不使用双引号) | - |
\xNN | 十六进制 ASCII 字符 | let c: u8 = b'\x41'; | A |
f32 & f64
类型 | 精度 | 范围 |
---|---|---|
f32 | IEEE单精度 (至少6位小数) | $\displaystyle -3.4 * 10^{38} \sim +3.4 * 10^{38}$ |
f64 | IEEE双精度 (至少15位小数) | $\displaystyle -1.8 * 10^{308} \sim +1.8 * 10^{308}$ |
字面量表示:
- 整数部分
- 浮点部分
- 指数部分
- 规定: 至少存在三个部分中的一个
- 其他表示: 5.省略后续也可
- 类型解析默认f64

浮点数::特殊关联常量
名称 | 说明 |
---|---|
f32::INFINITY | 无穷大 |
f32::NEG_INFINITY | 负无穷大 |
f32::NAN | 非数值 |
f32::MIN | 最小有限值 |
f32::MAX | 最大有限值 |
标准库运算
方法 | 描述 |
---|---|
pow(4) | x的四次方 |
abs() | 绝对值 |
count_ones() | 二进制中位1的个数 |
fn main() {
assert_eq!(2_u16.pow(4), 16);
assert_eq!((-4_i32).abs(), 4);
assert_eq!(0b101101_u8.count_ones(), 4);
}
3.2 bool类型
Rust对于bool具有非常严格的要求
- while, if等必须是用bool
- bool可以转换为整形true = 1, false = 0
- 其他类型不能转换为bool
- bool在内存中长度为一个字节, 所以可以拥有指针
3.3 字符类型
char是32位表示的Unicode字符
- 单独字符: 使用char类型
- 字符串和文本流使用UTF-8: String使用UTF-8
转义字符 (单引号)
转义字符 | 描述 | 示例代码 | 输出 |
---|---|---|---|
\' | 单引号本身 | let c = '\''; | ' |
\\ | 反斜杠 | let c = '\\'; | \ |
\n | 换行符 | let c = '\n'; | 换行 |
\r | 回车符 | let c = '\r'; | 回车 |
\t | 水平制表符 | let c = '\t'; | 制表符 |
\0 | 空字符(空字节) | let c = '\0'; | 空字符 |
\xNN | 十六进制 ASCII 字符 | let c = '\x41'; | A |
\u{NNNN} | Unicode 字符(1-6位十六进制) | let c = '\u{1F600}'; | 😀 |
码点表示:
- 如果码点属于ASCII: 直接写作
'\xHH'
- 范围更广的表示方法 (Unicode):
'\u{HHHHHH}'
两种转换方式:
- as: as只执行开销极低且可靠的转换, 由于Unicode的码表范围问题, u16和u32都可能出现非法转换的情况, 所以只有u8可以直接转化为char
0x32_u8 as char
std::char::from_u32(num: u32)->Option<char>
- 非法转换: 返回None
- 成功转换: 返回Some(c)
#Rust---
其他细节见: P123
3.4 元组
名称 | 说明 |
---|---|
元组定义 | 定义: 各种类型值的元素序列 实例: ("Brazil", 1985) 的类型 = (&str, i32) |
元组元素访问 #Rust-point | 只允许使用常量索引: t.0 : 访问下标为0的元素t.1 : 访问下标为1的元素 |
常见用法 | 1. 通过元组从函数中返回多个值fn split_at(&self, mid: usize)->(&str, &str); 2. 使用模式匹配将元组中的每个元素赋值给变量 let (head, tail) = text.split_at(21); // head和tail可以通过类型解析得到其类型为&str 3. 作为羽量级的结构体使用 fn write_image(filename: &str, bounds: (usize, usize)) -> Result<(), std::io::Error>; |
零元组 (单元类型) | 定义: 因为此类型只有一个值所以也称为单元类型() 用法: 用于表示"无值"或者"空值"的情况 fn swap<T>(x: &mut T, y: &mut T); 相当于 fn swap<T>(x: &mut T, y: &mut T) -> (); |
尾随逗号 | 在Rust中, 可以使用逗号的地方(数组, 参数, 结构体, 枚举)都可以包含尾随逗号(&str, i32,) 与 (&str, i32) 相同1. 让多行列表添加或者删除entry时更加清晰 2. 区分括号表达式和单元素的元组 ("hello") 为括号表达式中的字符串, ("hello", ) 为元组 |
尾随逗号的功能
不使用尾随逗号的多行列表情况: 需要在上一行添加逗号, diff: 修改一行代码 且 新增一行代码, diff不清晰
// 修改前
let items = [
"apple",
"banana",
"cherry"
];
// 修改后
let items = [
"apple",
"banana",
"cherry", // 需要在这里加上逗号
"date"
];
使用尾随逗号的多行列表情况: 只需要添加新的一行代码, diff只会提示新增一行代码, 更加清晰
// 修改前
let items = [
"apple",
"banana",
"cherry",
];
// 修改后
let items = [
"apple",
"banana",
"cherry",
"date", // 今天加了一行代码
];
3.5 指针类型
Rust的栈分配和Java的垃圾回收机制区别
具体代码在解释下方
Java下的Rectangle的upperLeft字段是一个引用, 实际的Vector2D对象存储在堆中, 对象内容之间是解耦的. 缺点是:
- 引用占用了额外的内存
- 解耦导致内存利用并不紧凑
- 内存分配和回收需要额外的开销
Rust下的Rectangle将直接嵌套upperLeft字段, 例如Vector2D是(i32, i32)
, 则Rectangle会嵌套为((i32, i32)(i32, i32))
优点是:
- 没有引用占用额外内存
- 内存利用更加紧凑
- 无需在堆中分配和回收内存, 减少开销
总结: Rust将内存回收通过所有权机制进行简化, 减少了运行时系统开销.
Java类代码
class Vector2D {
int x, y;
}
class Rectangle {
Vector2D upperLeft;
Vector2D lowerRight;
}
Rust 结构体代码
struct Vector2D {
x: i32,
y: i32,
}
struct Rectangle {
upper_left: Vector2D,
lower_right: Vector2D,
}
3.5.1 引用
引用类似于指针, 指向一个值的地址, 但是引用最大的特点是: 永远不能为空.
共享引用和可变引用的关系 ≈ 读者和写者
引用类型 | 说明 |
---|---|
不可变引用/共享引用 | 数量: 可以同时存在多个 (不能存在可变引用) 权限: 只读 |
可变引用 | 数量: 只能存在一个 (且共享引用也不能存在) 权限: 读写 |
3.5.2 Box
Box<T> 是一种用于在堆上分配数据的 智能指针,它提供了对值的所有权和指针语义,并在值超出作用域时自动释放堆内存。Box 是 Rust 中最基础的智能指针之一,常用于需要在堆上存储数据的场景。
堆中分配值最简单的方式就是使用Box::new()
let t = (12, "egg");
let b = Box::new(t); // t类型为(i32, &str), 所以b类型为Box<(i32, &str)>
b超出作用域时, 内存立即释放, 除非b已经被移动 (move)
3.5.3 裸指针
Rust裸指针*mut T
, *const T
类似于C指针, 裸指针并不安全, 它不会跟踪其指向的内容, 可能为空.
裸指针的解引用只能在unsafe块中执行.
3.6 数组, 向量和切片
三种值序列类型
序列类型 | 代码表示 | 说明 |
---|---|---|
数组 | [T; N] | T表示数组类型 N表示数组长度, 长度在编译阶段确定, 后续无法修改 |
向量 | Vec<T> | 动态分配的可增长的值序列, 元素保存在堆中, 可以随机增加或者减少元素 |
切片 | 共享切片: &[T] 可变切片: &mut [T] | 本质: 数组或者向量的一部分的引用 |
特性:
- 共同的方法
v.len()
都可以得到长度 - rust会检查下标越界, 如果越界产生panic
- 数组长度可以是0, 在这种情况下任意索引访问都将产生panic
- 索引类型必须是usize
3.6.1 数组 (array)
特性 | 说明 |
---|---|
数组每一个元素都必须初始化 | 错误代码: let mut a: [u8; 10]; : 报错binding declared here but left uninitialized复杂初始化可以使用iter和迭代器 |
数组特定填充值 | 将T类型替换为特定类型的填充值, 放在初始值的位置let mut sieve = [true; 10000]; |
array调用的方法属于slice | 所有的使用方法: 遍历, 填充, 排序, 搜索, len()等方法都是slice的方法, 但是Rust会自动将array转化为slice并调用对应的方法 调用流程: 1. Rust隐式生成一个可变切片&mut [i32] 2. 可变切片调用sort()方法 |
数组声明和初始化
fn main() {
let lazy_caterer: [u32; 6] = [1, 2, 4, 7, 11, 16]; // 显示指定类型
// 类型推断: 即推断元素类型,
// 同时按照初始化值的最小长度推断数组长度
let taxonomy = ["Animalia", "Arthropoda", "Insecta"];
assert_eq!(lazy_caterer[3], 7);
assert_eq!(taxonomy.len(), 3);
}
数组填充
#Rust-point
let mut sieve = [true; 10000]; // 填充true, 且长度为10000
for i in 2..100 {
let mut j = i * i;
while j < 10000 {
sieve[j] = false;
j += i;
}
}
assert!(sieve[211]);
assert!(!sieve[9876]);
slice的sort()方法
let mut chaos = [3, 5, 4, 1, 2];
chaos.sort();
assert_eq!(chaos, [1, 2, 3, 4, 5]);
3.6.2 向量 (vector)
特性 | 说明 |
---|---|
vector定义 | 变长T类型元素的数组 (堆分配) |
vector构成 | 1. 指向堆中缓冲区的指针 2. 容量 (capacity) 3. 当前元素个数 (len) |
vector创建 (初始化) | vec!宏: let mut primes = vec![2, 3, 5, 7]; 它提供了一个非常类似于数组的初始化方式 vec!宏填充: vec![0; rows * cols] // 类似数组的填充 迭代器生成: let v: Vec<i32> = (0..5).collect(); 注意使用迭代器时需要声明类型, 这样才能知道创建array还是vector capacity创建: 无需初始化, 因为capacity只是vector的容量, 而不是实际的元素长度, len仍然为0. let mut v = Vec::with_capacity(2); |
vector常用操作 | 添加元素: primes.push(11); 查看当前元素个数: primes.len(); 查看容量: primes.capacity(); 队尾添加元素: primes.push(1); 队尾弹出元素 (返回的是 Option<T> ): primes.pop(); 插入元素到对应下标: primes.insert(3, 35); 删除对应下标的元素: primes.remove(1); |
vector调用的方法属于slice | palindrome.reverse(); : 逆序元素排列 |
vec!宏解释 | 1. 调用Vec::new()创建空vector: let mut pal = Vec::new(); 2. 逐个压入数据: pal.push("step"); |
测试代码 (vector基本使用和常用切片方法)
// fn new_pixel_buffer(rows: usize, cols: usize) -> Vec<u8> {
// vec![0; rows * cols] // 类似数组的填充操作
// }
fn main() {
let mut primes = vec![2, 3, 5, 7];
assert_eq!(primes.iter().product::<i32>(), 210); // 计算所有元素的乘积
primes.push(11);
primes.push(13);
assert_eq!(primes.iter().product::<i32>(), 30_030);
let mut pal = Vec::new();
pal.push("step");
pal.push("on");
pal.push("no");
pal.push("pets");
assert_eq!(pal, vec!["step", "on", "no", "pets"]);
// let v: Vec<i32> = (0..5).collect();
// 回文
let mut palindrome = vec!["a man", "a plan", "a canal", "panama"];
palindrome.reverse();
assert_eq!(palindrome, vec!["panama", "a canal", "a plan", "a man"]);
// 测试len()和capacity()
let mut v = Vec::with_capacity(2); // 无需初始化, 因为这是vector的capacity, 不是vector的length
// println!("value = {}", v[0]); 如果为初始化访问元素, Rust会检测越界
assert_eq!(v.len(), 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);
println!("capacity now is: {}", v.capacity());
v.pop();
assert_eq!(v, vec![1, 2]);
println!("capacity now is: {}", v.capacity());
// 插入35到3号位 (后续元素后移)
let mut v = vec![10, 20, 30, 40, 50];
v.insert(3, 35);
assert_eq!(v, vec![10, 20, 30, 35, 40, 50]);
// 移除1号位的元素
v.remove(1);
assert_eq!(v, vec![10, 30, 35, 40, 50]);
}
测试代码 (参数args获取和遍历向量)
fn main() {
let languages: Vec<String> = std::env::args().skip(1).collect();
for l in languages {
println!("{}: {}", l,
if l.len() % 2 == 0 {
"functional"
} else {
"imperative"
}
);
}
}
3.6.3 切片 (slice)
#Rust-dif
特性 | 说明 |
---|---|
切片的构成 | 是一个"胖指针", 即附带了其他信息的指针: 1. 指向切片首个元素的指针 2. 切牌中元素的数量 |
切片长度任意的限制 | 1. 切片不能是变量: 如果切片作为变量相当于动态长度的数组 (数组的长度只能是静态的) 2. 切片不能作为参数传递: 切片直接作为参数, 则函数无法获取切片的长度 |
普通引用, 切片引用, 向量引用, 数组引用的区别 #Rust-point | 1. 普通引用: 指向单个值的非拥有型指针 2. 切片引用: 只含有实际数据指针和长度两个部分, 提供对数组或向量的一部分的只读权限, 切片的长度取决于初始化 3. 向量引用: 相比于切片引用多了一个capacity 4. 数组引用: 只能引用整个数组, 不能引用部分 |
切片内存布局图
let v: Vec<f64> = vec![0.0, 0.707, 1.0, 0.707];
let a: [f64; 4] = [0.0, -0.707, 1.0, -0.707];
let sv: &[f64] = &v;
let sa: &[f64] = &a;

3.7 字符串类型
类似于C++中const char *
和String
两种表示字符串的方式
Rust中也有两种表示字符串的方式
3.7.1 字符串字面量
字符串字面量特性
特性 | 说明 | 代码 |
---|---|---|
双引号表示 | 字符串字面量使用双引号表示, 其中单引号无需转义 | println!("Hello, world!"); |
跨越多行 | 字符串字面量可以跨越多行, 且会识别空白符 (空格, 换行) | println!("Hello, world!"); |
忽略换行和前导空白符 | println!("Hello,\ world!"); | |
原始字符串 | Windows路径下需要反斜杠符号, 每次均需要\\ 转义, 使用原始字符串特性: 所有空白符和反斜杠包含在字符串中. | r"C:\program files\hello" |
原始字符串确定起始 | 问题: 反斜杠转义符失效了, 如果原始字符串中包含"就会导致原始字符串无法确定起始位置 解决方法: 使用**任意数量的#**表示起始位置 | r###"Tom say: "Hello!""### |
转义字符 (双引号)
转义字符 | 描述 | 示例代码 | 输出 |
---|---|---|---|
\" | 双引号本身 | let s = "\""; | " |
\\ | 反斜杠 | let s = "\\"; | \ |
\n | 换行符 | let s = "\n"; | 换行 |
\r | 回车符 | let s = "\r"; | 回车 |
\t | 水平制表符 | let s = "\t"; | 制表符 |
\0 | 空字符(空字节) | let s = "\0"; | 空字符 |
\xNN | 十六进制 ASCII 字符 | let s = "\x41"; | A |
\u{NNNN} | Unicode 字符(1-6位十六进制) | let s = "\u{1F600}"; | 😀 |
3.7.2 字节串 (字节字符串)
字节串特性
特性 | 说明 |
---|---|
字节串定义 | 字节串是u8值的切片引用, 而不是Unicode文本 |
字节串字面量 | b"GET" 类型: &[u8; 3] |
字节串与字符串关系 | 1. 字面量语法相似 2. 字面量具有跨越多行, 原始字符串 br"" 的特性3. 不能包含Unicode, 只能使用ASCII或者 \xHH {=tex} |
3.7.3 内存中的字符串 (String详细说明)
特性
特性 | 说明 |
---|---|
编码格式 | String采用UTF-8的形式编码 |
String构成 | 类似于vector: 1. 指向字符串缓冲区的指针 2. 容量 (capacity) 3. 长度 (length): capacity - 1是因为需要' \x00 {=tex}'作为字符串结尾标识注意: 长度以字节而不是字符为单位 其中实际存放UTF-8字符串的堆缓冲区是可以调整大小的, 可以将 String 视为Vec<u8> (虽然UTF-8是变长的, 理解为void* ) |
创建String的方式 | 1. .to_string() : 将&str 转换为String : let error_message = "too many pets".to_string(); 2. .to_owned() : 同上, 这种方法适用于另一些特定类型3. format!() 宏: 类似于println!() , 返回一个新的String, 而不是文本输出4. 字符串数组: let bits = vec!["veni", "vidi", "vici"]; assert_eq!(bits.concat(), "venividivici"); assert_eq!(bits.join(", "), "veni, vidi, vici"); |
&str | string slice, 胖指针, 构成如下: 1. 指向切片的指针 2. 长度 (length) |
&str &mut str 关系 | 1. let test = "hello"; : test的类型是&str 2. let mut test = "hello"; : test的类型也是&str , 但是test本身可以改变, 作为其他的字符串的共享引用3. let test: &mut str = &mut s; : test的类型时&mut str , 但是能做的事情也不多, 因为切片引用不能修改缓冲区的长度, 只能执行make_ascii_uppercase 和 make_ascii_lowercase 改变大小写 |
&str String 功能区别 | 1. &str更适合函数参数传递 |
字符串内存实例
let noodles = "noodles".to_string(); // String
let oodles = &noodles[1..];
let poodles = "ಠ_ಠ"; // &str

3.7.4 String
String
类似于Vec<T>
参数 | Vec<T> | String |
---|---|---|
自动释放缓冲区 | ✓ | ✓ |
可增长 | ✓ | ✓ |
类型关联函数::new() 和::with_capacity() | ✓ | ✓ |
.reverse() 和.capacity() | ✓ | ✓ |
.push() 和pop() | ✓ | ✓ |
范围语法[start..stop] | ✓, 返回&[T] | ✓, 返回&str |
自动转换 | &Vec<T> 到 &[T] | &String 到&str |
继承的方法 | 来自&[T] | 来自&str |
3.7.5 使用字符串
特性
特性 | 说明 | 代码 |
---|---|---|
支持使用==和!= | 比较字符串中的内容是否相等 | assert!("ONE".to_ascii_lowercase() == "one"); |
还有其他的运算: >, <, <=, >=等 |
3.7.6 其他类似字符串的类型
类型 | 说明 |
---|---|
String 和&str | 使用Unicode文本 |
std::path::PathBuf 和&Path | 专用于文件路径 |
Vec<u8> 和&[u8] | 二进制数据 |
OsString 和 &OsStr | 操作系统提供的原生形式的环境变量名和命令行参数 |
std::ffi::CString 和 &CStr | 和使用 null 结尾字符串的 C 语言库进行互操作 |
3.8 类型别名
type Bytes = Vec<u8>;
fn decode(data: &Bytes) {
...
}
(整理) 各种类型的转义字符
类型 | 代码 | 链接 |
---|---|---|
u8 | b'' | [(Rust程序设计) Chapter3-基础数据类型#转义字符 u8]((Rust程序设计) Chapter3-基础数据类型#转义字符 u8 "wikilink") |
char | '' | [(Rust程序设计) Chapter3-基础数据类型#转义字符 单引号]((Rust程序设计) Chapter3-基础数据类型#转义字符 单引号 "wikilink") |
字符串 | "" | [(Rust程序设计) Chapter3-基础数据类型#转义字符 双引号]((Rust程序设计) Chapter3-基础数据类型#转义字符 双引号 "wikilink") |
TODO
- ☐ 类型推断的原理
- ☐ 出现二义性时如何推断: 编译器会报错
- ☐ String和&str在什么情况下使用: