这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
为什么使用智能指针?
很直接的原因:有些类型,你在编译期无法确定他的 size,但是 rust 需要你确定;因为不确定,没办法分配 → 分配到 heap 上,用一个 pointer 指向。
这个 pointer 本质:管理其所指向的对象的内存地址;由于没有 GC,所以还需要当对象不再被使用的时候会将其释放。
这个后续的行为消除了很多因不恰当的内存管理而引起的 bug,最主要的就是:保证安全。
智能指针可以像普通指针一样被解引用,但是在赋值(assignment)和析构(deallocation)时会表现出不同的行为。
本文,就来说说怎么用这个实现一个不确定大小的数据结构 ⇒ linkedlist
简单链表
链表就是 node1 → node2 → node3 → ... → null:
struct Node {
value: i32,
next: Option<Node>,
}
node可能为NULL,这个就需要用到Option<>- Rust 结构体在编译时必须是确定性大小,这个 next 继续链接 node。谁知道它要链接几个,所以无法知道其大小。
最直观的做法:Box<T> ⇒ 它在堆上分配数据,并且当它离开作用域的时候,指针和其指向的数据都会被丢弃(drop)
struct Node {
value: i32,
next: Box<Option<Node>>,
}
fn main() {
let a = Node {
value: 5,
next: Box::new(None),
};
let b = Node {
value: 10,
next: Box::new(Some(a)),
};
println!("b is {:?}", b);
}
⚠️:a → move to b,所以后续的代码你要使用 a,是会编译失败的
共享链表
前面说了,如果将 a → move to b,这个 a 在后续的使用上会编译失败。
如果要想 a 可以共存在多个链表,形成一个公有 node。Box<T> 肯定是不能在使用了。
这个时候 → Rc<T> 。Rc 指针通过 clone 来共享,clone 操作会创建一份(Rc的)拷贝,这份拷贝指向相同的数据并增加引用计数。当这些指针失效时,引用计数会减少
struct SharedNode {
value: i32,
next: Rc<Option<SharedNode>>,
}
use std::rc::Rc;
fn main() {
let a = Rc::new(Some(SharedNode {
value: 5,
next: Rc::new(None),
}));
let b = SharedNode {
value: 10,
next: Rc::clone(&a),
};
let c = SharedNode {
value: 50,
next: Rc::clone(&a),
};
println!("a is {:?}", a);
println!("b is {:?}", b);
println!("c is {:?}", c);
}
有几个问题:
- b 要借用 a,只能是拿到 a 的引用,但是在
next: Rc<Option<SharedNode>>,这里又不是引用 → 通过Rc::clone(),实现之间的共享 - clone() 这个,传入一个引用 → return 这个引用对应的实体的一份拷贝