文章目录
- 一、pattern matching
- 二、trait
- 2.1 常见 trait
- 2.1.1 Copy 和 Clone
- 2.1.2 PartialEq 和 Eq
- 2.1.3 PartialOrd 和 Ord
- 2.1.4 Hash
- 2.1.5 From, Into, TryFrom, TryInto
- 2.2 概念
- 2.2.1 关联类型
- 2.2.2 关联常量
- 2.3.3 泛型关联类型
- 2.3.3.1 示例: 用泛型关联类型, 创建集合工厂
- 2.3.3.2 在函数式编程中使用泛型关联类型
- 三 生命周期
- 3.1 子类型
- 3.2 高阶生命周期绑定(HRTB): 泛型的泛型
- 四 闭包
- 4.1 move 关键字: 强制捕获上下文的所有权
- 4.2 闭包的类型推断
- 4.3 闭包的 trait 的表示和存储
- 4.2.1 示例: 将闭包作为函数参数或返回值
- 4.2.2 示例: 将闭包存储到数据结构中
- 五 迭代器
- 5.1 示例: 消耗迭代器进行遍历的基本方法
- 5.2 迭代器的 trait
- 5.2.1 示例: 使用 trait 方法消耗迭代器
- 5.2.2 示例: 自定义计数器迭代器
- 5.3.3 展开 for 循环
- 5.4 迭代器的变换和消耗
- 5.4.1 示例: 变换和消耗
- 5.4.2 迭代器的性能
- 六 智能指针
- 6.1 智能指针的特征
- 6.2 智能指针的引用计数RC
- 6.3 内部可变性
- 6.3.1 RefCell
- 6.3.3.1 示例: 上例使用 RefCell 的效果
- 6.3.3.2 示例: 使用 Rc + RefCell 实现双向链表
- 七 并发与并行
- 7.1 进程和线程
- 7.2 多线程数据访问: 消息传递
- 7.3 多线程数据访问: 状态共享
- 7.4 多线程相关 trait: Send 和 Sync
- 7.4.1 使用 Arc 智能指针
- 八 unsafe Rust
- 8.1 裸指针 *const T 和 *mut T
- 8.2 ManuallyDrop<T>
- 8.3 NonNull<T>
- 8.4 MaybeUninit<T>
- 8.5 其他函数和方法
本地运行 rustup doc 即可打开 rust 官方文档
一、pattern matching
模式匹配
let a = 0;
match a {
0 => println!("zero"),
_ => println!("non-zero"),
}
// let 就是一种 pattern match 的表达式
#[warn(unused_variables)]
fn main() {
struct A {
x: i32,
y: &'static str,
z: B,
}
#[derive(Debug)]
enum B {
X(i32),
Y { a: u8, b: u8 },
Z,
}
let a = A {
x: 0,
y: "hello",
z: B::Y { a: 2, b: 3 },
};
let A { x, y, z } = a; // 模式匹配(即对 struct 的解构), xyz 都是新的变量, 将 a 赋值给他们(PS: 因为 struct A 中的属性为 xyz, 所以必须命名为 xyz)
println!("{}, {}, {:?}", x, y, z);
}
// code result:
0, hello, Y { a: 2, b: 3 }
// if let 的 pattern matching 匹配度更高
if let A {
z: B::Y { a, b }, .. // ..表示忽略其他字段
} = a
{}
if let (x, y, 3) = (10, 11, 12) {} // 若 3 != 12 则直接返回模式匹配
let a = 12;
let s = match a {
90..=100 => "A", // ..= 会自动转换为 std::ops::RangeInclusive 结构体
80..=89 => "B",
70..=89 => "C",
_ => "D",
};
// ..end 会自动转换为 core::ops::RangeTo, 即 x < end
// start.. 会自动转换为 core::ops::RangeFrom, 即 x > end
二、trait
2.1 常见 trait
2.1.1 Copy 和 Clone
trait core::clone::Clone 是一个对象, 其赋值对象完整的数据(当然也包括该对象的所有权)
所有不可变引用都实现了 Clone trait(因为不可变引用可以被持有多份, 所以可以被 Clone)
Clone is a supertrait of Copy, so everything which is Copy must also implement Clone. If a type is Copy then its Clone implementation only needs to return *self (see the example above).
We can derive a Copy
implementation. Clone
is also required, as it’s a supertrait of Copy
.
-
因为实现了 Copy 和 Clone, 所以 let b = a 并未转移所有权, 所以最后一行 a 仍可使用(仍在栈上), 如下图:
-
而若没有实现 Copy, 则会报错, 因为 let b = a 所有权转移后, 无法再访问 a 了, 如下图:
-
而若显式调用 .clone() 即可通过编译, 如下图:
-
如果定义了 A.ptr 指向堆上的内存, 则只能用Clone trait 的 clone()
-
错误用法如下: Copy trait 会造成 a.ptr 和 b.ptr 都指向同一份堆上的内存, 造成垂悬引用, 虽然 rust 编译器不会报错, 但这是不安全的, 而应当用智能指针来解决. 如下图:
2.1.2 PartialEq 和 Eq
Eq implies PartialEq
区别
PartialEq 描述的是部分相等关系(离散数学的偏序关系), 如 float 中 Nan 和 Nan 是无法比较的, 所以 float 是 PartialEq
Eq 描述的是完全相等关系(离散数学的全序关系)
只要实现了 PartialEq, 就可用 a == b 和 a != b 来表达
PartialEq 是离线数学的偏序关系, 若 a == b
, 且 b == c
, 无法推导出 a == c
Eq 是离线数学的全序关系, 若 a == b
, 且 b == c
, 能推导出 a == c
如果想表达部分相等关系, 则只实现 PartialEq 即可
如果想表达完全相等关系, 则实现 PartialEq 后, 再用 impl Eq for Book {}
实现默认的 Eq 方法即可
#[warn(dead_code)]
fn main() {
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
struct Book {
isbn: i32,
format: BookFormat,
}
impl PartialEq for Book {
fn eq(&self, other: &Self) -> bool {
self.isbn == other.isbn
}
}
impl Eq for Book {}
let a = Book {
isbn: 1,
format: BookFormat::Paperback,
};
let b = Book {
isbn: 1,
format: BookFormat::Hardback,
};
assert!(a == b)
}
fn main() {
let f1 = 3.14;
let f2 = 3.15;
if f1 == f2 {
println!("same");
}
if f1 != f2 {
println!("not same");
}
is_eq(f1); // 编译不通过, 因为 float 仅实现了 PartialEq, 而因为 Nan 无法比较故未实现 Eq
is_partial_eq(f1);
}
fn is_eq<T: Eq>(f: T) {}
fn is_partial_eq<T: PartialEq>(f: T) {}
不同类型也可各自实现 PartialEq, 从而比较 type1 == type2, 但是传递性会有问题, 如下例:
#[derive(PartialEq)]
enum BookFormat {
Paperback,
Hardback,
Ebook,
}
#[derive(PartialEq)]
struct Book {
isbn: i32,
format: BookFormat,
}
impl PartialEq<BookFormat> for Book {
fn eq(&self, other: &BookFormat) -> bool {
self.format == *other
}
}
impl PartialEq<Book> for BookFormat {
fn eq(&self, other: &Book) -> bool {
*self == other.format
}
}
fn main() {
let b1 = Book { isbn: 1, format: BookFormat::Paperback };
let b2 = Book { isbn: 2, format: BookFormat::Paperback };
assert!(b1 == BookFormat::Paperback);
assert!(BookFormat::Paperback == b2);
// The following should hold by transitivity but doesn't.
assert!(b1 == b2); // <-- PANICS
}
2.1.3 PartialOrd 和 Ord
PartialOrd 是离线数学的偏序关系, 若 a < b
, 且 b < c
, 无法推导出 a < c
Ord 是离线数学的全序关系, 若 a < b
, 且 b < c
, 能推导出 a < c
2.1.4 Hash
#[derive(Hash)]
struct Rustacean {
name: String,
country: String,
}
也可以自定义用某些字段做 Hash, 示例如下:
use std::hash::{Hash, Hasher};
struct Person {
id: u32,
name: String,
phone: u64,
}
impl Hash for Person {
fn hash<H: Hasher>(&self, state: &mut H) {
self.id.hash(state);
self.phone.hash(state);
}
}
2.1.5 From, Into, TryFrom, TryInto
TryFrom 定义了错误处理:
pub trait TryFrom<T>: Sized {
type Error;
// Required method
fn try_from(value: T) -> Result<Self, Self::Error>;
}
struct GreaterThanZero(i32);
impl TryFrom<i32> for GreaterThanZero {
type Error = &'static str;
fn try_from(value: i32) -> Result<Self, Self::Error> {
if value <= 0 {
Err("GreaterThanZero only accepts values greater than zero!")
} else {
Ok(GreaterThanZero(value))
}
}
}
let big_number = 1_000_000_000_000i64;
// Silently truncates `big_number`, requires detecting
// and handling the truncation after the fact.
let smaller_number = big_number as i32;
assert_eq!(smaller_number, -727379968);
// Returns an error because `big_number` is too big to
// fit in an `i32`.
let try_smaller_number = i32::try_from(big_number);
assert!(try_smaller_number.is_err());
// Returns `Ok(3)`.
let try_successful_smaller_number = i32::try_from(3);
assert!(try_successful_smaller_number.is_ok());
2.2 概念
本质: 表示一组特定的功能
包含: 方法, 关联类型, 常量
2.2.1 关联类型
例如如下自行实现的一个 函数 trait:
fn main() {
trait Function<Args> {
// Args: 输入参数
type Output; // Output: 关联类型
fn call(args: Args) -> Self::Output;
}
struct AddFunction; // 单元结构体, 其内存布局相当于()
// 两个数的加法, 入参为(T, T), 关联类型为 Output = T
impl<T: std::ops::Add<Output = T>> Function<(T, T)> for AddFunction {
type Output = T;
fn call(args: (T, T)) -> Self::Output {
args.0 + args.1
}
}
// 三个数的加法, 入参为(T, T), 关联类型为 Output = T
impl<T: std::ops::Add<Output = T>> Function<(T, T, T)> for AddFunction {
type Output = T;
fn call(args: (T, T, T)) -> Self::Output {
args.0 + args.1 + args.2
}
}
// 虽两种 AddFunction 的泛型实现, 但关联类型 Output 只有一种
assert_eq!(AddFunction::call((1, 2)), 3);
assert_eq!(AddFunction::call((1, 2, 3)), 6);
}
2.2.2 关联常量
// 下文抽象了寄存器访问内存地址
use std::marker::PhantomData;
fn main() {
//------如下为定义------
enum Access {
Read,
Write,
ReadWrite,
}
trait MmioReg {
const OFFSET: usize; // 关联常量
const ACCESS: Access; // 关联常量
type Repr: Sized + Copy; // 关联类型 Repr 意为寄存器
fn at(base: std::ptr::NonNull<()>) -> Volatile<Self::Repr> {
let _address = base.as_ptr() as usize + Self::OFFSET;
Volatile(Self::ACCESS, PhantomData)
}
}
struct Volatile<T>(Access, std::marker::PhantomData<T>); // struct, 标识用什么方式(Access), 读写一个内存指针(PhantomData)
//------如下为使用------
struct SampleReg;
impl MmioReg for SampleReg {
const OFFSET: usize = 0x100;
const ACCESS: Access = Access::ReadWrite;
type Repr = u32;
}
let reg = SampleReg::at(std::ptr::NonNull::dangling());
}
2.3.3 泛型关联类型
trait 内的关联类型, 本身也可以是泛型的
2.3.3.1 示例: 用泛型关联类型, 创建集合工厂
fn main() {
// -------定义trait如下
trait CollectionFactory {
type Collection<T>;
type Iter<'a, T>
where
T: 'a;
fn empty<T>() -> Self::Collection<T>;
fn iter<'a, T>(collection: &'a Self::Collection<T>) -> Self::Iter<'a, T>
where
T: 'a;
}
// -------实现trait如下
struct VecFactory;
impl CollectionFactory for VecFactory {
type Collection<T> = Vec<T>;
type Iter<'a, T> = std::slice::Iter<'a, T> where T: 'a;
fn empty<T>() -> Self::Collection<T> {
Vec::new()
}
fn iter<'a, T>(collection: &'a Self::Collection<T>) -> Self::Iter<'a, T>
where
T: 'a,
{
collection.iter()
}
}
// ------使用如下, 传入 T 和 CollectionFactory
struct Queue<T, F: CollectionFactory> {
inner: F::Collection<T>,
}
let vec_based_queue = Queue::<_, VecFactory> {
inner: vec![1, 2, 3],
};
}
2.3.3.2 在函数式编程中使用泛型关联类型
fn main() {
trait Functor<T> {
type Output<U>: Functor<U>;
fn fmap<U>(self, f: impl FnOnce(T) -> U) -> Self::Output<U>;
}
impl<T> Functor<T> for Option<T> {
type Output<U> = Option<U>;
fn fmap<U>(self, f: impl FnOnce(T) -> U) -> Self::Output<U> {
self.map(f)
}
}
let s = Some(1);
let s = s.fmap(|x| x + 1);
assert_eq!(s, Some(2));
}
三 生命周期
本质: 一个结构单元, 其包括了一个变量/类型的存活范围
fn main() {
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
let l = longest("a", "abc");
assert_eq!(l, "abc");
}
fn main() {
struct Ref<'a, T>(&'a T);
let x = 42;
let y = Ref(&x);
assert_eq!(y.0, &42);
}
3.1 子类型
- 生命周期的包含关系: 比 … 活得久(outlive)
- 结论:
'a: 'b
等价于'a 比 'b 活得久, 即 a outlives b
- 定理: 对于任意生命周期 'a, 有 'static: 'a (即 staitc 生命周期 比 a 生命周期 活的更久)
- 结论:
- 子类型: 可替换性
- 定义: 'x 是 'y 的子类型
- 大前提: 对于某处 “不可变引用”, 有对于单个生命周期的要求
- 小前提: 若 'y 满足该要求
- 结论: 'x 也满足该要求
- 暴论: 类型越"子", 可以满足的要求越多
- 定义: 'x 是 'y 的子类型
- 结合以上两组结论, 我们有
'a: 'b
等价于'a 是 'b 的子类型
将 'a 是 'b 的子类型
表示为 偏序关系 <, 'a 与 'b 无关 表示为 'a <> 'b, 则对于任意两个生命周期 'a 和 'b, 仅存在 3 种不等关系, 将这3种关系统称为 R('a, 'b):
- 'a < 'b, 即 a 是 b 的子类型
- 'a <> 'b, 即 a 与 b 无关
- 'a > 'b, 即 b 是 a 的子类型
则 R 对于泛型的映射
- 对于单生命周期的泛型 T, 这个映射即定义为 T: R('a, 'b) --> R<'a>, T<'b>), 这个关系有三种:
- 相同: 即若 'a 是 'b 的子类型, 则 T<'a> 是 T<'b> 的子类型
- 相反: 即若 'a 是 'b 的子类型, 则 T<'b> 是 T<'a> 的子类型
- 无关: 即若 'a 是 'b 的子类型, 则 T<'a> 和 T<'b> 无关
示例如下:
fn main() {
struct A<'a>(&'a i32); // A即为一个单生命周期的类型构造器
let x = 1;
// 外括号的生命周期为'a
{
let y: &i32 = &x;
// 内括号的生命周期为'b
{
let z: &i32 = &x;
}
}
// 因为外括号的存活域比内括号的存活域大, 设外括号的生命周期为'a, 设内括号的生命周期为'b
// 则 'a 是 'b 的子类型
// 则对于实际构造出的两个引用(y 和 z), 即有 y 是生命周期为'a 的 &i32, z 是生命周期为'b 的 &i32
}
更详细的解释是:
fn main() {
struct A<'a>(&'a i32); // A即为一个单生命周期的类型构造器
let x = 1;
// 外括号的生命周期为'a
{
let mut y: &i32 = &x;
// 内括号的生命周期为'b
{
let mut z: &i32 = &x;
y = z; // 可编译通过
z = y; // 可编译通过
}
}
// 因为外括号的存活域比内括号的存活域大, 设外括号的生命周期为'a, 设内括号的生命周期为'b
// 则 'a 是 'b 的子类型
// 则对于实际构造出的两个引用(y 和 z), 即有 y 是生命周期为'a 的 &i32, z 是生命周期为'b 的 &i32
// 则 y 的生命周期为 'a, z 的生命周期为 'b
// 则生命周期为 'b 的 &i32, 可以被 生命周期为 'a 的 &i32 替换
// 生命周期为 'a 的 &i32, 就是生命周期为 'b 的 &i32 (子类型 a 就是父类型 b)
// 构成了一种映射: 若 'a 是 'b 的子类型, 则 &'a &i32 是 &'b &i32 的子类型
}
总结三种映射关系如下:
- 不可变引用, 是协变关系 (示例如上)
- 如 &'static str 是 &str 的子类型, 即 &'static str 可替换 &str
- 函数, 是逆变关系
- 如 fn(&'static str) 并不是 fn(&'x str) 的子类型, 即不能用 fn(&'static str) 替换 fn(&'x str).
- 但反而 fn(&'x str) 是 fn(&'static str) 的子类型, 即能用 fn(&'x str) 替换 fn(&'static str)
- 可变引用, 是不变关系
示例: 如下图即为悬垂引用的示例
- 用传统 rust 话术是这样解释的: 因为 hello 是 static 生命周期, 而 world 只有 'x 的生命周期, 所以 当将 hello 指向 world 的引用时, rust 编译器会报错: world does not live long enough, 即当 world 走出其作用域而失效时, 会造成 hello 指向悬垂引用:
- 另一种解释话术如下:
详见 file:///Users/y/.rustup/toolchains/stable-aarch64-apple-darwin/share/doc/rust/html/nomicon/subtyping.html?highlight=subtyping#subtyping
3.2 高阶生命周期绑定(HRTB): 泛型的泛型
四 闭包
本质: 拥有可能的关联上下文的匿名函数结构体
fn main() {
let x = 5;
let plus_x = |a| a + x;
assert_eq!(plus_x(5), 10);
}
闭包实际展开如下:
#[test]
fn expansion() {
struct Anonymous<'a>(&'a i32);
impl<'a> Anonymous<'a> {
// 捕获
fn new(x: &'a i32) -> Self {
Anonymous(x)
}
// 使用
fn call(&self, a: i32) -> i32 {
a + self.0
}
}
let x = 5;
let plus_x = Anonymous::new(&x);
assert_eq!(plus_x.call(5), 10);
}
4.1 move 关键字: 强制捕获上下文的所有权
- 不使用 move 时如下:
#[test]
fn use_keyword_move() {
let vec = vec![1, 2, 3];
{
let print_borrowed_vec = || println!("{vec:?}"); // 不使用 move 关键字时, 闭包是以引用方式捕获的
println!("{vec:?}");
print_borrowed_vec()
}
}
- 使用 move 时如下:
#[test]
fn use_keyword_move() {
let vec = vec![1, 2, 3];
{
let print_owned_vec = move || println!("{vec:?}");
println!("{vec:?}"); // 编译报错, 因vec 的所有权已在上一行被转移了
print_owned_vec();
}
}
move 其实是一个语法糖, 我们也可自己捕获, 如下例是等价的两个闭包:
fn use_keyword_move() {
let vec = vec![1, 2, 3];
// {
// let print_owned_vec = move || println!("{vec:?}");
// print_owned_vec();
// }
{
let print_owned_vec = || {
let vec = vec; // vec 捕获了所有权
println!("{vec:?}");
};
print_owned_vec();
}
}
4.2 闭包的类型推断
4.3 闭包的 trait 的表示和存储
// FnOnce 示例如下:
let print_owned_vec = move || println!("{vec:?}"); // 是FnOnce, 即调用一次就会消耗其所有权
// FnMut 示例如下:
struct Anonymous<'a>(&'a mut i32); // 是 &mut, 所以每调用一次都会使其上下文发生改变, 则它只实现了 FnMut, 而没有实现 Fn
impl<'a> Anonymous<'a> {
// 捕获
fn new(x: &'a i32) -> Self {
Anonymous(x)
}
// 使用
fn call(&self, a: i32) -> i32 {
a + self.0
}
}
// Fn 示例如下:
let plus_x = |a| a + x; // 是Fn, 因为获取的都是不可变引用, 所以其可被无损的调用任意次
他们之间是包含关系:
- 只要实现了 Fn, 则一定也实现了 FnMut 和 FnOnce
- 只要实现了 FnMut, 则一定也实现了 FnOnce
4.2.1 示例: 将闭包作为函数参数或返回值
#[test]
fn pass_as_parameters_and_return_values() {
fn plus(x: i32) -> impl Fn(i32) -> i32 {
move |a| a + x
}
fn map<T, U>(value: T, f: impl FnOnce(T) -> U) -> U {
f(value)
}
fn assert_fn_once<F: FnOnce(i32) -> i32>(_: F) {}
fn assert_fn_mut<F: FnMut(i32) -> i32>(_: F) {}
let x = 5;
let plus_x = plus(x); // 因为 plus_x 实现了 Fn, 又因为若实现了 Fn 则一定也实现了 FnMut 和 FnOnce
assert_fn_once(&plus_x); // 所以 plus_x 实现了 FnOnce, 所以此断言通过
assert_fn_mut(&plus_x);
assert_eq!(map(2, plus_x), 7);
}
4.2.2 示例: 将闭包存储到数据结构中
#[test]
fn storage() {
// -----定义:
struct ServiceBuilder<Opener> {
params: Option<String>,
opener: Opener,
}
impl<Opener> ServiceBuilder<Opener> {
fn new(opener: Opener) -> Self {
Self {
params: None,
opener,
}
}
fn with_params(mut self, params: String) -> Self {
self.params = Some(params);
self
}
}
impl<Service, Opener: FnOnce(String) -> Service> ServiceBuilder<Opener> {
fn open(self) -> Service {
let params = self.params.expect("Please input some params");
(self.opener)(params)
}
}
// -----使用:
let opener = |params| {
println!("Opening service with params: {params}");
"mocked service"
};
let init_service = ServiceBuilder::new(opener).with_params("Some params".to_string());
let service = init_service.open();
println!("Service is {service:?}");
}
// output result as belows:
Opening service with params: Some params
Service is "mocked service"
test storage ... ok
五 迭代器
本质: 对序列的流失处理–变换或消耗
5.1 示例: 消耗迭代器进行遍历的基本方法
#[test]
fn basic_usage() {
let array = [1, 2, 3, 4, 5];
let iter = array.iter();
for item in iter {
println!("{item}");
}
}
5.2 迭代器的 trait
5.2.1 示例: 使用 trait 方法消耗迭代器
#[test]
fn iterator_traits() {
let array = [1, 2, 3, 4, 5];
let mut iter = array.iter();
assert_eq!(iter.next(), Some(&1));
assert_eq!(iter.next(), Some(&2));
assert_eq!(iter.next(), Some(&3));
assert_eq!(iter.next(), Some(&4));
assert_eq!(iter.next(), Some(&5));
assert_eq!(iter.next(), None);
assert_eq!(iter.next(), None);
}
5.2.2 示例: 自定义计数器迭代器
#[test]
fn custom_iterator() {
struct Counter {
start: i32,
count: i32,
}
impl Iterator for Counter {
type Item = i32;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 0 {
let ret = self.start;
self.start += 1;
self.count -= 1;
Some(ret)
} else {
None
}
}
}
let mut counter = Counter { start: 3, count: 5 };
for item in counter {
println!("{item}",);
}
}
// code result:
3
4
5
6
7
5.3.3 展开 for 循环
#[test]
fn expand_for_loop() {
let array = [1, 2, 3, 4, 5];
for item in array.iter() {
println!("{item}");
}
// 可展开为 while let
{
let mut iter = array.iter();
while let Some(item) = iter.next() {
println!("{item}");
}
}
// 也可展开为 loop match
let mut iter = array.iter();
loop {
match iter.next() {
Some(item) => println!("{item}"),
None => break,
}
}
}
5.4 迭代器的变换和消耗
迭代器是 rust 函数式编程经常使用的场合
5.4.1 示例: 变换和消耗
fn main() {
struct Person {
name: String,
age: i32,
}
let people = [
Person {
name: "Alice".to_string(),
age: 21,
},
Person {
name: "Bob".to_string(),
age: 25,
},
Person {
name: "Charlie".to_string(),
age: 18,
},
Person {
name: "Dave".to_string(),
age: 30,
},
Person {
name: "Eve".to_string(),
age: 28,
},
Person {
name: "Frank".to_string(),
age: 32,
},
];
let student_ids = [1001, 1002, 1003, 1004, 1005, 1006];
let all_names = people.iter().map(|p| p.name.clone()).collect::<Vec<_>>();
println!("{:?}", all_names);
// let first_3_student_ids_whose_age_is_over_21
let mut first_3_student_ids_whose_age_is_over_21 = Vec::new();
for index in 0..6 {
let person = &people[index];
let id = student_ids[index];
if person.age > 21 {
first_3_student_ids_whose_age_is_over_21.push(id);
if first_3_student_ids_whose_age_is_over_21.len() == 3 {
break;
}
}
}
assert_eq!(first_3_student_ids_whose_age_is_over_21, [1002, 1004, 1005]);
let first_3_student_ids_whose_age_is_over_21 = people
.iter()
.zip(student_ids.iter())
//.filter(|(p, _)| p.age > 21)
//.map(|(_, id)| *id).
.filter_map(|(p, &id)| (p.age > 21).then_some(id))
.take(3)
.collect::<Vec<_>>();
assert_eq!(first_3_student_ids_whose_age_is_over_21, [1002, 1004, 1005]);
}
5.4.2 迭代器的性能
迭代器性能很好, 程序员不需要为了性能而使用 for, 使用 迭代器即可兼顾性能和可读性
六 智能指针
fn main() {}
#[test]
fn basic_usage() {
let x = 5;
let boxed_x = Box::new(x); // Box将无法在编译期确定大小的东西, 变为可在编译器确定大小
let y = *boxed_x; // 解引用
let z = *boxed_x;
assert_eq!(y, z);
}
- Box
use std::collections::HashMap;
fn main() {}
#[test]
fn basic_usage() {
let x = 5;
let boxed_x = Box::new(x);
let y = *boxed_x;
let z = *boxed_x;
assert_eq!(y, z);
let array = [1, 2, 3, 4, 5];
let mut iter = Box::new(array.into_iter()) as Box<dyn Iterator<Item = i32>>;
let hash_map = [(1, ()), (2, ())].into_iter().collect::<HashMap<_, _>>();
iter = Box::new(hash_map.into_keys());
}
- vec
fn main() {}
#[test]
fn basic_usage() {
let x = 5;
let boxed_x = Box::new(x);
let y = *boxed_x;
let z = *boxed_x;
assert_eq!(y, z);
let array = [1, 2, 3, 4, 5];
let slice = &array[1..3];
let vec = slice.to_vec();
let vec_slice = &vec[..];
assert_eq!(vec_slice, slice);
}
- String
fn main() {}
#[test]
fn basic_usage() {
let x = 5;
let boxed_x = Box::new(x);
let y = *boxed_x;
let z = *boxed_x;
assert_eq!(y, z);
let s = "Hello world";
let s_slice = &s[1..3];
let string = s.to_string();
let string_slice = &string[1..3];
assert_eq!(string_slice, s_slice);
}
6.1 智能指针的特征
源码中 String 的 Deref 实现如下:
- 自己实现的 智能指针 Smart 如下, 主要是实现了 Deref trait:
use std::ops::Deref;
fn main() {}
#[test]
fn custom_smart_pointers() {
struct Smart<T>(T);
impl<T> Smart<T> {
fn new(value: T) -> Self {
Self(value)
}
}
impl<T> Deref for Smart<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
let smart = Smart::new(5);
assert_eq!(*smart, 5)
}
- 自己实现的 智能指针 Smart 如下, 除了实现 Deref trait, 还实现了 DerefMut trait:
use std::ops::{Deref, DerefMut};
fn main() {}
#[test]
fn custom_smart_pointers() {
struct Smart<T>(T);
impl<T> Smart<T> {
fn new(value: T) -> Self {
Self(value)
}
}
impl<T> Deref for Smart<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Smart<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
let mut smart = Smart::new(5);
assert_eq!(*smart, 5);
*smart = 10;
assert_eq!(*smart, 10);
}
6.2 智能指针的引用计数RC
use std::rc::Rc;
fn main() {
let c = Node::new(1, None);
let b = Node::new(2, Some(Rc::clone(&c)));
let a = Node::new(3, Some(Rc::clone(&b)));
let x = Node::new(4, Some(Rc::clone(&a)));
let y = Node::new(5, Some(Rc::clone(&a)));
// 因为 x 和 y 都共享 a, 所以有两条链表, 分别是 xabc 和 yabc
let vec_x = x.to_vec();
assert_eq!(vec_x, vec![4, 3, 2, 1]);
let vec_y = y.to_vec_iterated();
assert_eq!(vec_y, vec![5, 3, 2, 1]);
}
struct Node<T> {
data: T,
next: Option<Rc<Node<T>>>,
}
impl<T> Node<T> {
fn new(data: T, next: Option<Rc<Node<T>>>) -> Rc<Self> {
Rc::new(Node { data, next })
}
fn to_vec(self: &Rc<Self>) -> Vec<T>
where
T: Clone,
{
let mut vec = Vec::new();
let mut node = Some(Rc::clone(self));
while let Some(n) = node {
vec.push(n.data.clone());
node = n.next.as_ref().map(Rc::clone);
}
vec
}
// -----使用迭代器如下:
fn to_vec_iterated(self: &Rc<Self>) -> Vec<T>
where
T: Clone,
{
let mut node = Some(Rc::clone(self));
let iter = std::iter::from_fn(move || match node.take() {
Some(n) => {
node = n.next.as_ref().map(Rc::clone);
Some(n.data.clone())
}
None => None,
});
iter.collect()
}
}
6.3 内部可变性
fn main() {}
#[test]
fn interior_mutability() {
#![allow(unused)]
trait MessageSender {
fn send(&self, message: String); // &self 是不可变引用
}
struct Validator<M: MessageSender>(M);
impl<M: MessageSender> Validator<M> {
fn validate(&self, value: &i32) {
if !(-10..10).contains(value) {
self.0
.send(format!("Value {} is not in range [-10, 10]", value));
}
}
}
struct MockMessageSender {
messages: Vec<String>,
}
impl MessageSender for MockMessageSender {
fn send(&self, message: String) {
self.messages.push(message); // 因为需要调用 push(), 所以需要 self 内部的 messages 是可变的, 而 &self 本身是不可变引用, 所以需要内部可变性
}
}
}
6.3.1 RefCell
本质是先在编译期通过, 但运行时如果发生错误仍会直接 panic(), 所以非常坏的一种实践
6.3.3.1 示例: 上例使用 RefCell 的效果
6.3.3.2 示例: 使用 Rc + RefCell 实现双向链表
七 并发与并行
7.1 进程和线程
- 示例: 使用 std::thread 创建线程, 使用 JoinHandle() 等待线程完成
use std::thread;
fn main() {}
#[test]
fn use_multithread() {
let thread = thread::spawn(|| {
for i in 0..10 {
println!("thread: {}", i);
}
});
for i in 0..10 {
println!("main: {}", i);
}
thread.join().unwrap();
}
// code result:
main: 0
main: 1
main: 2
main: 3
main: 4
main: 5
main: 6
main: 7
main: 8
main: 9
thread: 0
thread: 1
thread: 2
thread: 3
thread: 4
thread: 5
thread: 6
thread: 7
thread: 8
- 示例: 使用 move 在多线程间用闭包捕获所有权
use std::thread;
fn main() {}
#[test]
fn move_between_threads() {
let vec = vec![1, 2, 3, 4, 5];
let thread = thread::spawn(move || {
let mut vec = vec;
vec.push(6);
println!("{vec:?}");
});
// println!("{vec:?}"); // 此处无法再使用 vec, 因为其所有权已被 move 到线程里了
thread.join().unwrap();
}
// code result:
[1, 2, 3, 4, 5, 6]
7.2 多线程数据访问: 消息传递
uuse std::{sync::mpsc, thread};
fn main() {}
#[test]
fn mpsc_channels() {
let (tx, rx) = mpsc::channel();
for t in 0..10 {
let tx = tx.clone();
thread::spawn(move || {
for i in 0..3 {
let num = 100 * t + i;
tx.send(num).unwrap();
}
});
}
drop(tx); // drop main()函数中的 tx
// 当所有发送端都发送完毕后, 则 mpsc 的所有 rx 都会自动 drop, 则rx.recv() 会返回 Err
while let Ok(num) = rx.recv() {
println!("{}", num);
}
}
7.3 多线程数据访问: 状态共享
use std::{
sync::{mpsc, Mutex},
thread,
time::Duration,
};
fn main() {}
#[test]
fn mutexes() {
static DATA: Mutex<i32> = Mutex::new(0);
thread::spawn(|| {
for i in 0..10 {
*DATA.lock().unwrap() += i;
thread::sleep(Duration::from_secs(1));
}
});
while *DATA.lock().unwrap() < 45 {
println!("Waiting");
thread::sleep(Duration::from_secs(1));
}
}
// code result: 打印 10 次 Waiting 后值大于 45, 故退出 main() 函数
running 1 test
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
test mutexes ... ok
7.4 多线程相关 trait: Send 和 Sync
7.4.1 使用 Arc 智能指针
Arc 即 Atomic 的 Rc, 可在线程间使用
use std::{
sync::{mpsc, Mutex, Arc},
thread,
time::Duration,
};
fn main() {}
#[test]
fn arc_usage() {
let data = Arc::new(Mutex::new(0));
let d2 = data.clone();
thread::spawn(move || {
for i in 0..10 {
*d2.lock().unwrap() += i;
thread::sleep(Duration::from_secs(1));
}
});
while *data.lock().unwrap() < 45 {
println!("Waiting");
thread::sleep(Duration::from_secs(1));
}
}
Arc 源码如下:
八 unsafe Rust
unsafe 块一定要写注释, 示例如下: