博主未授权任何人或组织机构转载博主任何原创文章,感谢各位对原创的支持!
博主链接
博客内容主要围绕:
5G/6G协议讲解
高级C语言讲解
Rust语言讲解
文章目录
- Rust中的所有权介绍
-
- [1.1 一个简单的例子](#1.1 一个简单的例子)
- [1.2 一个稍微复杂的例子](#1.2 一个稍微复杂的例子)
- [1.3 Rust 所有权的特点](#1.3 Rust 所有权的特点)
- [1.4 局限性和Rust提供的解决方法](#1.4 局限性和Rust提供的解决方法)
Rust中的所有权介绍
在 Rust 中,所有权
这个概念内置于语言本身,并通过编译期检查强制执行。每个值都有决定其生命周期的唯一拥有者。当拥有者被释放时,它拥有的值也会同时被释放,在 Rust 术语中,释放的行为被称为丢弃(drop)。这些规则便于通过检查代码确定任意值的生命周期,也提供了系统级语言本应支持的对生命周期的控制。
1.1 一个简单的例子
例如下面的代码,变量拥有自己的值,当控制流离开声明变量的块时,变量就会被丢弃,因此它的值也会一起被丢弃:
rust
fn print_padovan() {
let mut padovan = vec![1,1,1]; // 在此分配
for i in 3..10 {
let next = padovan[i-3] + padovan[i-2];
padovan.push(next);
}
println!("P(1..10) = {:?}", padovan);
} // 在此丢弃
变量 padovan 的类型为 Vec<i32>,即一个 32 位整数向量。padovan 的内存结构如下图:
这和之前展示过的 C++ std::string 非常相似,不过缓冲区中的元素都是 32 位整数,而不是字符。请注意,保存 padovan 指针、容量和长度的字都直接位于 print_padovan 函数的栈帧中,只有向量的缓冲区才分配在堆上。
和之前的字符串 s 一样,此向量拥有保存其元素的缓冲区。当变量 padovan 在函数末尾超出作用域时,程序将会丢弃此向量。因为向量拥有自己的缓冲区,所以此缓冲区也会一起被丢弃。
Rust 的 Box
类型是所有权的另一个例子。Box<T> 是指向存储在堆上的 T 类型值的指针。可以调用 Box::new(v) 分配一些堆空间,将值 v 移入其中,并返回一个指向该堆空间的 Box。因为 Box 拥有它所指向的空间,所以当丢弃 Box 时,也会释放此空间。
1.2 一个稍微复杂的例子
再来看一个稍微复杂一点的例子:
rust
struct Person { name: String, birth: i32 }
let mut composers = Vec::new();
composers.push(Person { name: "Palestrina".to_string(),
birth: 1525 });
composers.push(Person { name: "Dowland".to_string(),
birth: 1563 });
composers.push(Person { name: "Lully".to_string(),
birth: 1632 });
for composer in &composers {
println!("{}, born {}", composer.name, composer.birth);
}
代码中 composers 是一个 Vec<Person>,即由结构体组成的向量,每个结构体都包含一个字符串和数值。composers 的内存结构如下所示:
这里有很多所有权关系,但每个都一目了然:composers 拥有一个向量,向量拥有自己的元素,每个元素都是一个 Person 结构体,每个结构体都拥有自己的字段,并且字符串字段拥有自己的文本。当控制流离开声明 composers 的作用域时,程序会丢弃自己的值并将整棵所有权树一起丢弃。如果还存在其他类型的集合(可能是 HashMap 或 BTreeSet),那么处理的方式也是一样的。
1.3 Rust 所有权的特点
现在,回过头来思考一下刚刚介绍的这些所有权关系的重要性。每个值都有一个唯一的拥有者,因此很容易决定何时丢弃它。但是每个值可能会拥有许多其他值,比如向量 composers 会拥有自己的所有元素。这些值还可能拥有其他值:composers 的每个元素都各自拥有一个字符串,该字符串又拥有自己的文本。
由此可见,拥有者及其拥有的那些值形成了一棵树:值的拥有者是值的父节点,值拥有的值是值的子节点 。每棵树的总根都是一个变量,当该变量超出作用域时,整棵树都将随之消失。可以在 composers 的图中看到这样的所有权树:它既不是"搜索树"那种数据结构意义上的"树",也不是由 DOM 元素构成的 HTML 文档。相反,我们有一棵由混合类型构建的树,Rust 的单一拥有者规则将禁止任何可能让它们排列得比树结构更复杂的可能性。Rust 程序中的每一个值都是某棵树的成员,树根是某个变量。
Rust 程序通常不需要像 C 程序和 C++ 程序那样显式地使用 free 和 delete 来丢弃值。在 Rust 中丢弃一个值的方式就是从所有权树中移除它,或者离开变量的作用域,或者从向量中删除一个元素,或者执行其他类似的操作。这样一来,Rust 就会确保正确地丢弃该值及其拥有的一切。
1.4 局限性和Rust提供的解决方法
从某种意义上说,Rust 确实不如其他语言强大:其他所有实用的编程语言都允许你构建出任意复杂的对象图,这些对象可以用你认为合适的方式相互引用。但正是因为 Rust 不那么强大,所以编辑器对你的程序所进行的分析才能更强大。Rust 的安全保证之所以可行,是因为在你的代码中可能出现的那些关系都更可控。
我们已经解释过的这些所有权概念仍然过于严格,还处理不了某些场景。Rust 从几个方面扩展了这种简单的思想:
- 可以将值从一个拥有者
转移
给另一个拥有者。这允许你构建、重新排列和拆除树形结构; - 像整数、浮点数和字符这样的非常简单的类型,不受所有权规则的约束。这些称为
Copy 类型
; - 标准库提供了
引用计数指针类型 Rc 和 Arc
,它们允许值在某些限制下有多个拥有者; - 可以对值进行
"借用"(borrow)
,以获得值的引用。这种引用是非拥有型指针,有着受限的生命周期。