Rust 智能指针「基本链表表达」

1,737 阅读2分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

为什么使用智能指针?

很直接的原因:有些类型,你在编译期无法确定他的 size,但是 rust 需要你确定;因为不确定,没办法分配 → 分配到 heap 上,用一个 pointer 指向。

这个 pointer 本质:管理其所指向的对象的内存地址;由于没有 GC,所以还需要当对象不再被使用的时候会将其释放。

这个后续的行为消除了很多因不恰当的内存管理而引起的 bug,最主要的就是:保证安全。

智能指针可以像普通指针一样被解引用,但是在赋值(assignment)和析构(deallocation)时会表现出不同的行为。

本文,就来说说怎么用这个实现一个不确定大小的数据结构 ⇒ linkedlist

简单链表

链表就是 node1 → node2 → node3 → ... → null:

struct Node {
    value: i32,
    next: Option<Node>,
}
  1. node 可能为NULL,这个就需要用到 Option<>
  2. 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);
}

有几个问题:

  1. b 要借用 a,只能是拿到 a 的引用,但是在 next: Rc<Option<SharedNode>> ,这里又不是引用 → 通过 Rc::clone() ,实现之间的共享
  2. clone() 这个,传入一个引用 → return 这个引用对应的实体的一份拷贝