rust所有权与借用
Rust :所有权(Ownership)
所有权是 Rust 最核心的概念之一,也是 Rust 与 Python、JavaScript、Java 等语言的重要区别。
一、什么是所有权?
Rust 的所有权(Ownership)是一套管理内存的规则。
它让 Rust 在不依赖垃圾回收器(GC)的情况下,仍然能够保证内存安全。
1. 为什么需要所有权?
程序运行时,需要使用内存存储数据。
常见语言管理内存的方式:
| 语言 | 内存管理方式 |
|---|---|
| C | 手动分配和释放 |
| C++ | 手动管理、RAII、智能指针 |
| Java | 垃圾回收(GC) |
| Python | 引用计数 + 垃圾回收 |
| JavaScript | 垃圾回收(GC) |
| Rust | 所有权 + 借用 + 生命周期 |
Rust 的特点是:
在编译阶段检查大部分内存使用规则,避免很多运行时内存错误。
2. 所有权的三条核心规则
必须掌握:
- Rust 中的每个值都有一个所有者(Owner)。
- 同一时刻,一个值只能有一个所有者。
- 当所有者离开作用域(Scope),这个值会被清理。
这里的第二条是针对普通所有权模型而言的。学习 Rc<T> 等智能指针时,还会接触共享所有权。
二、作用域(Scope)
先看最简单的代码。
1 | |
执行过程:
示意图:变量只在自己的作用域内有效。
当程序执行到内部作用域的 } 时:
word离开作用域。- Rust 自动调用清理逻辑(对于
String,释放其拥有的堆内存)。 - 后续不能再访问
word。
可以把作用域理解为变量的有效范围。
三、所有权转移(Move)
这是本章第一个难点。
1. 普通整数赋值
1 | |
输出:
1 | |
为什么 a 还能使用?
因为整数 i32 实现了 Copy trait,赋值时会复制它的值。
2. String 赋值
1 | |
这里会编译失败。
原因是:
1 | |
将 word1 的所有权转移给了 word2。
Move 后堆中的字符串数据没有被复制,所有权交给 word2,word1 不再可用。
为什么 Rust 要这样设计?
因为 String 包含指向堆内存的指针、长度和容量。如果简单复制这些管理信息,两个变量就可能试图释放同一块内存。
Rust 通过所有权转移,避免这种问题。
四、克隆(Clone)
如果想保留两个独立的字符串怎么办?
使用 .clone()。
1 | |
输出:
1 | |
clone() 会复制字符串数据,因此两个变量各自拥有独立的数据。
| 操作 | 所有权转移 | 复制字符串数据 |
|---|---|---|
let b = a;(String) |
是 | 否 |
let b = a.clone(); |
否 | 是 |
let b = a;(i32) |
否 | 是(Copy) |
注意:clone() 可能涉及堆内存分配,因此不应该为了绕开所有权问题而无脑使用。
五、借用(Borrowing)
假设你只想读取一个单词,而不想取得它的所有权。
这时可以使用引用 &。
1 | |
输出:
1 | |
这里:
word拥有字符串。borrowed_word借用了字符串。- 所有权没有转移。
1. 函数中的借用
1 | |
输出:
1 | |
函数参数 word: &String 表示接收一个 String 的不可变引用。
实际开发中,只需要读取字符串内容时,通常更推荐使用 &str:
1 | |
它既可以接收 String 的借用,也可以接收字符串字面量,使用起来更灵活。
六、可变借用(Mutable Borrowing)
普通引用只能读取,不能修改。
如果需要修改字符串,使用 &mut。
1 | |
输出:
1 | |
注意两个地方:
1 | |
声明变量可以修改。
1 | |
创建可变引用。
借用规则
Rust 对借用有严格限制:
- 同一时间可以存在多个不可变引用
&T。 - 或者存在一个可变引用
&mut T。 - 不能同时使用相互冲突的可变和不可变借用。
- 引用必须始终有效,不能悬垂。
例如:
1 | |
这段代码可以编译。
因为 r1 和 r2 最后一次使用结束后,才创建 r3。
Rust 的借用检查并不是简单地认为引用必须一直存在到所在代码块结束,而是会分析引用的最后使用位置。
七、函数与所有权
函数参数也会发生所有权转移。
1 | |
因为 consume_word() 接收的是 String,所以调用函数时所有权发生转移。
函数执行结束后,参数 word 离开作用域,字符串会被释放。
如果函数只需要读取数据,应该优先使用引用:
1 | |
另外,函数返回 String 时,也可以将所有权交给调用者:
1 | |
八、所有权知识总结
| 语法 | 含义 | 原变量能否继续使用 |
|---|---|---|
let b = a; |
Move(非 Copy 类型) | 否 |
let b = a.clone(); |
克隆 | 是 |
let b = &a; |
不可变借用 | 是,受借用规则限制 |
let b = &mut a; |
可变借用 | 是,借用结束后 |
fn f(a: String) |
函数取得所有权 | 否 |
fn f(a: &str) |
函数借用字符串 | 是 |
fn f(a: &mut String) |
函数可变借用 | 是,借用结束后 |
