学习rust_day11

Cargo Workspaces
Workspace(工作空间)是一组共享同一个Cargo.lock文件和输出目录的包
Vorkspace例子:

  • 一个Binary crate: main
  • 两个Library Crate:
    • add_one
    • add two

例:

在项目顶级文件夹下创建Cargo.toml:

1
2
3
4
5
[workspace]

members = [
"adder"
]

使用命令:

1
cargo new adder

此时得到顶级文件夹下adder的子文件夹,该文件夹含有自己的toml和src。

添加一个lib_crate:

1
cargo new add_one --lib

此时顶级Cargo.toml:

1
2
3
4
5
6
[workspace]

members = [
"adder",
"add_one",
]

让adder依赖add_one

adder/Cargo.toml:

1
2
3
4
5
6
7
[package]
name = "adder"
version = "0.1.0"
edition = "2024"

[dependencies]
add_one ={path = "../add_one"}

解决解析器警告

Cargo.toml:

1
2
3
4
5
6
7
8
[workspace]

members = [
"adder",
"add_one",
]

resolver = "3"

指定执行:

1
cargo run -p adder

workspace中只有顶层一个Cargo.lock,为了解决项目同依赖的问题。

但是你仍然需要在每个子文件夹下的 Cargo.toml单独标明互相使用的外部包,比如adder依赖add_one,而add_one依赖rand,所以你也得在adder中写明依赖于rand。


Smart Pointers 智能指针

指针:比如引用,只有指向地址的功能

智能指针:带有额外的元数据和功能

引用仅借用数据,智能指针通常拥有数据。

Box

允许你将数据存储在Heap上

场景:

  • 在需要知道确切大小的上下文中,却使用一个在编译时无法确定大小的类型
  • 有大量数据,想要转移所有权,但需确保在转移时数据不会被复制
  • 当你想要拥有一个值,且你只关心它是否实现了某Trait,而不是具体的类型

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
use List::{Cons, Nil};

fn main() {
let b = Box::new(5);
println!("{b}");

let list = Cons(1, Box::new(Cons(2, Box::new(Cons(3, Box::new(Nil))))));
}

enum List {
Cons(i32, Box<List>),
Nil
}

Deref Trait

  • Deref trait允许你自定义解引用运算符 * 的行为
  • 通过适当实现Deref trait,可以让智能指针像普通引用来使用
  • 你编写的用于引用的代码,也能用于智能指针

实现 Deref trait 后可以把一个类型像引用一样来使用。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use std::ops::Deref;

fn main() {
let x = 5;
let y = MyBox::new(x);

assert_eq!(5, x);
assert_eq!(5, *y);
}

struct MyBox<T>(T);

impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}

impl <T> Deref for MyBox<T> {
type Target = T; //关联类型

fn deref(&self) -> &Self::Target {
&self.0
}
}

Deref coercion 隐式解引用转换

  • 隐式解引用转换能将实现了Deref trait的类型的引用转换为另一个类型的引用
    • 例如:由于String实现了返回&str的Deref trait,所以可以将&String转换为&str
  • 编写函数和方法调用时,不需要添加太多显式的&和*
  • 允许编写能同时适用于引用或智能指针的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
use std::ops::Deref;

fn main() {
let m = MyBox::new(String::from("Rust"));
hello("Hello World");
hello(&m); //&MyBox<String> --> &String --> &str
}

fn hello(name: &str) {
println!("Hello, {}!", name);
}

struct MyBox<T>(T);

impl<T> MyBox<T> {
fn new(x: T) -> MyBox<T> {
MyBox(x)
}
}

impl <T> Deref for MyBox<T> {
type Target = T; //关联类型

fn deref(&self) -> &Self::Target {
&self.0
}
}

Deref Coercion与可变性

  • Deref:对不可变引用的*的重载
  • DerefMut:对可变引用的*的重载

三种解引用转换场景:

  • &T->&U (T:Deref<Target=U>)
  • &mut T->&mut U (T:DerefMut<Target=U>)
  • &mut T->&U (T:Deref<Target=U>)

Drop Trait

  • 用于定义一个值即将超出作用域时的清理行为
  • 实现智能指针时几乎总是会用到Drop trait的功能
  • Rust 编译器会自动插入Drop 实现中的代码,避免资源泄漏
  • Drop trait 只要求实现 drop 方法,参数是对 self 的可变引l用
  • Drop trait在prelude里,无需手动引l入

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct CustomSmartPointer {
data: String,
}

impl Drop for CustomSmartPointer {
fn drop(&mut self) {
println!("{}", self.data);
}
}

fn main() {
let c = CustomSmartPointer {
data: String::from("my stuff")
};

let d = CustomSmartPointer {
data: String::from("other stuff")
};
println!("Start");
}

释放顺序与创建顺序相反。

使用std::mem:.drop可提前drop值

  • Rust不允许手动调用Drop trait的drop方法
  • 如果你想在值的作用域结束前强制丢弃它,你必须调用标准库提供的 std:mem:drop函数
  • std:mem:drop的调用不会干扰Rust的自动清理机制
    • 因为它通过接管值的所有权(ownership)并在调用后销毁它,避免了双重释放(double free)问题

手动销毁某变量:

1
drop(name);

Rc 引用计数智能指针

Reference Counting

  • 有些情况下,一个值可能有多个所有者
  • RC 可以开启“多重所有权”
    • 跟踪一个值的引用数量,可判断该值是否还在使用
      • 如果没有引用了,就可以清理掉了

Rc的使用场景

  • 想在Heap上分配一些数据,供程序的多个部分读取,但在编译时无法确定哪个部分会最后完成对数据的使用
  • 只可用于单线程场景

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
use crate::List::{Cons, Nil};
use std::rc::Rc;

enum List {
Cons(i32, Rc<List>),
Nil
}

fn main() {
let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
println!("{}", Rc::strong_count(&a)); //a引用的数量 1
let b = Cons(3, Rc::clone(&a));
println!("{}", Rc::strong_count(&a)); //a引用的数量 2
{
let c = Cons(4, Rc::clone(&a));
println!("{}", Rc::strong_count(&a)); //a引用的数量 3
}
println!("{}", Rc::strong_count(&a)); //a引用的数量 2
}

RefCell 和内部可变性模式

  • 对数据不可变引用时也可以修改数据
  • 数据结构内部使用unsafe代码来绕过Rust通常的规则
  • unsafe代码:手动检查规则,而不是依赖编译器
  • 只有当能保证在运行时借用规则被遵守时,才可使用内部可变性模式的类型

在运行时通过RefCell<-T>强制执行借用规则

  • RefCell类型表示对其所持有数据的单一所有权
  • 回顾借用规则:
    • 在任何时刻,要么拥有一个可变引用,要么任意数量的不可变引用
    • 引用必须始终有效

只适用单线程

当确信你的代码是安全的,但是编译器无法理解或保证时你才能使用这个。

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
pub trait Messenger {
fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T: Messenger> {
messenger: &'a T,
value: usize,
max: usize
}

impl<'a, T> LimitTracker<'a, T>
where
T: Messenger,
{
pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
LimitTracker {
messenger,
value: 0,
max
}
}

pub fn set_value(&mut self, value: usize) {
self.value = value;

let percentage_of_max = self.value as f64 / self.max as f64;

if percentage_of_max >= 1.0 {
self.messenger.send("Error: 1");
} else if percentage_of_max >= 0.9 {
self.messenger.send("Error: 2");
} else if percentage_of_max >= 0.75 {
self.messenger.send("Error: 3");
}
}
}

#[cfg(test)]
mod tests {
use super::*;
use std::cell::RefCell;

struct MockMessenger {
sent_messages: RefCell<Vec<String>>,
}

impl MockMessenger {
fn new() -> MockMessenger {
MockMessenger {
sent_messages: RefCell::new(vec![]),
}
}
}

impl Messenger for MockMessenger {
fn send(&self, message: &str) {
self.sent_messages.borrow_mut().push(String::from(message));
}
}

#[test]
fn it_sends_on_over_75_percent_warning_message() {
let mock_messenger = MockMessenger::new();
let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);

limit_tracker.set_value(80);

assert_eq!(mock_messenger.sent_messages.borrow().len(), 1);
}
}

组合Rc 和 RefCell 实现多个所有者对可变数据的访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
use crate::List::{Cons, Nil};
use std::rc::Rc;
use std::cell::{Ref, RefCell};

#[derive(Debug)]

enum List {
Cons(Rc<RefCell<i32>>, Rc<List>),
Nil
}

fn main() {
let value = Rc::new(RefCell::new(5));

let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));

let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));

*value.borrow_mut() += 10;

println!("{a:?}");
println!("{b:?}");
println!("{c:?}");
}

学习rust_day11
https://zlsf-zl.github.io/2026/03/20/学习rust-day11/
作者
ZLSF
发布于
2026年3月20日
许可协议