智能指针(一)

在各个编程语言中,指针的概念几乎都是相同的:指针是一个包含了内存地址的变量,该内存地址引用或者指向了另外的数据

在 Rust 中,最常见的指针类型是引用,通过 & 符号表示。不同于其它语言,引用在 Rust 中被赋予了更深层次的含义:借用其它变量的值
引用本身很简单,除了指向某个值外并没有其它的功能,也不会造成性能上的额外损耗,因此是 Rust 中使用最多的指针类型。

智能指针虽然也号称指针(胖指针),但是它是一个复杂的数据类型:通过比引用更复杂的数据结构,包含比引用更多的信息,例如元数据,当前长度,最大可用长度等。在 C++ 或者其他语言中也存在智能指针相似的概念。

前面提到过:
不能简单的将变量与类型视为只是一块栈内存或一块堆内存数据,比如 Vec 类型,rust 将其分成两部分数据:存储在堆中的实际类型数据与存储在栈上的管理信息数据。
其中存储在栈上的管理信息数据是引用类型,包含实际类型数据的地址、元素的数量,分配的空间等信息,rust 通过栈上的管理信息数据掌控实际类型数据的信息

1
2
3
let v = vec![1, 2, 3];
let vp = &v;
println!("{:p}, {:p}", &v, &vp);

上面的案例中 v 栈内存存储就是一个智能指针(胖指针),通过 println!("{:p}", &v) 可获取指针信息。

功能上,引用在 rust 中被赋予更深的含义:借用其它变量的值,而智能指针比引用更强大:

  • 提供比引用更多的功能特性,例如引用计数智能指针,该智能指针允许你同时拥有同一个数据的多个所有权,它会跟踪每一个所有者并进行计数,当所有的所有者都归还后,该智能指针及指向的数据将自动被清理释放。
  • 提供比引用更多的服务特性,引用仅仅是借用了数据,而智能指针往往可以拥有它们指向的数据,然后再为其它人提供服务。例如动态字符串 String 和动态数组 Vec,它们的数据结构中不仅仅包含了指向底层数据的指针,还包含了当前长度、最大长度等信息,其中 String 智能指针还提供了一种担保信息:所有的数据都是合法的 UTF-8 格式。

智能指针往往是基于结构体实现,它与自定义的结构体最大的区别在于它实现了 Deref 和 Drop 特征:

  • Deref 可以让智能指针像引用那样工作,这样你就可以写出同时支持智能指针和引用的代码,例如 *T。正如 Index 特征,可以为自定义结构体实现 Index 特征,然后可以使用 [number] 操作。
  • Drop 允许你指定智能指针超出作用域后自动执行的代码,例如做一些数据清除等收尾工作。

智能指针在 Rust 中很常见,几个最常用、最有代表性的智能指针:

  • Box<T>,可以将值分配到堆上
  • Rc<T>,引用计数类型,允许多所有权存在
  • Ref<T>RefMut<T>,允许将借用规则检查从编译期移动到运行期进行

Code

1
2
3
4
5
fn main() {
let v = vec![1, 2, 3];
let vp = &v;
println!("{:p}, {:p}, {:p}", v.as_ptr(), &v, &vp);
}