Rust 语言最独特也最核心的特性就是它的内存管理方式,它通过"所有权"(Ownership)和"借用"(Borrowing)系统来确保内存安全,而不需要像 Go 那样依赖垃圾回收器。
1. 所有权 (Ownership)
你可以把所有权想象成你拥有一件物品的权利。在 Rust 中,每块数据都有一个"所有者"。这个所有者是唯一的,并且当所有者不再需要这块数据时(比如变量超出了它的作用范围),Rust 会自动帮你清理掉这块数据所占用的内存。
所有权的核心规则有三条:
- 每个值都有一个所有者。 就像你买了一本书,这本书就属于你。
- 在任何给定时间,一个值只能有一个所有者。 你不能同时把同一本书借给两个人,让他们都声称自己是这本书的唯一主人。
- 当所有者超出作用域时,值会被"丢弃"(内存被释放)。 当你不再需要这本书时,你会把它处理掉(比如卖掉、捐掉或扔掉),它所占用的空间就被释放了。
rust
fn main() {
let s1 = String::from("你好,Rust!"); // s1 现在是这串文本的所有者
// println!("{}", s1); // 此时 s1 拥有所有权,可以正常使用
let s2 = s1; // 所有权从 s1 转移到了 s2
// println!("{}", s1); // 错误!s1 不再拥有所有权,不能再使用了 [1]
println!("{}", s2); // s2 现在是所有者,可以正常使用 [1]
} // 当 s2 超出作用域时,它所拥有的数据("你好,Rust!")会被自动清理 [1]
2. 借用 (Borrowing)
如果每次使用数据都必须转移所有权,那会非常不方便。比如,你只是想让朋友帮你看看你的书,而不是把书送给他。这时,你就可以把书"借"给朋友。在 Rust 中,这就是"借用"的概念。
借用允许你临时访问一个值,而不需要获取它的所有权 。当你借用一个值时,你会得到一个"引用"(Reference),而不是拥有这个值本身。
借用的类型:
Rust 有两种类型的借用(或引用):
- 不可变借用 (
&T
) :这就像你把书借给朋友看,他可以阅读书的内容,但不能在书上涂写或撕页。在 Rust 中,这意味着你可以读取被借用的数据,但不能修改它 。 - 可变借用 (
&mut T
) :这就像你把书借给朋友,并允许他在书上做笔记或修改。在 Rust 中,这意味着你可以读取并修改被借用的数据 。
rust
fn main() {
let s = String::from("Hello"); // s 拥有所有权
// 不可变借用
let len = calculate_length(&s); // 我们把 s 的引用(&s)传递给函数
println!("字符串 '{}' 的长度是 {}.", s, len); // s 仍然拥有所有权,可以继续使用 [1]
// 可变借用
let mut s_mut = String::from("Hello"); // s_mut 拥有所有权,并且是可变的
change_string(&mut s_mut); // 我们把 s_mut 的可变引用(&mut s_mut)传递给函数
println!("{}", s_mut); // s_mut 仍然拥有所有权,并且内容已被修改 [1]
}
fn calculate_length(s: &String) -> usize { // 接收一个不可变引用
s.len() // 可以读取长度
} // s 在这里超出作用域,但因为它只是一个引用,所以它指向的数据不会被丢弃 [1]
fn change_string(s: &mut String) { // 接收一个可变引用
s.push_str(", world!"); // 可以修改字符串内容
}
3. 借用规则 (Borrowing Rules)
为了确保内存安全,Rust 对借用施加了非常严格的规则,这些规则在编译时就会被检查:
-
在任何给定时间,你只能拥有以下两种情况之一:
- 一个可变引用:就像你只能把书借给一个朋友,并允许他修改,这样就不会有两个人同时修改同一本书导致混乱的情况。
- 任意数量的不可变引用:你可以把书复印很多份,借给很多朋友阅读,因为他们都不能修改原书,所以不会有冲突 。
-
引用必须始终有效:你不能有一个"悬垂引用"(dangling reference),即引用指向的内存已经被释放了。Rust 的借用检查器会确保引用不会比它所指向的数据活得更久 。
rust
fn main() {
let mut s = String::from("hello"); // s 是可变的,拥有所有权
let r1 = &s; // 没问题,第一个不可变引用
let r2 = &s; // 没问题,第二个不可变引用
println!("{} 和 {}", r1, r2); // r1 和 r2 在这里被使用,之后就不再使用了
// let r3 = &mut s; // 错误!不能在不可变引用 (r1, r2) 仍然活跃时创建可变引用 [1]
// 因为 r1 和 r2 在上一行 println! 之后就不再使用了,所以现在可以创建可变引用
let r3 = &mut s; // 没问题,现在可以创建可变引用了 [1]
r3.push_str(", world!");
println!("{}", r3);
}
总结
-
所有权:数据有且只有一个所有者,所有者负责数据的生命周期和内存清理。
-
借用:允许你临时访问数据而不转移所有权,通过"引用"实现。
-
引用 :分为不可变引用(
&T
,只读)和可变引用(&mut T
,可读写)。 -
借用规则:严格限制了引用的使用方式,确保在编译时就避免了数据竞争和内存安全问题。
vibe coding
- here is myjy tools website