4.1 什么是所有权
一、所有权是什么?
所有权是 Rust 管理内存的一套编译时规则,它让 Rust 无需 GC 也能保证内存安全,且不损失性能。核心只有三条:
- Rust 中每个值都有且只有一个所有者(owner)。
- 同一时刻,值只能有一个所有者。
- 当所有者离开作用域,值会被自动丢弃(调用
drop释放内存)。
二、栈与堆的区分
- 栈:存放编译时大小固定的数据(整数、浮点、
bool、char等),复制快,离开作用域时直接弹出。 - 堆:存放编译时大小未知或可变的数据(如
String),需要分配器动态分配,访问需通过指针,稍慢。
所有权主要管理的是堆上的数据。
三、移动(move)与拷贝(copy)
1. 移动:String 的赋值
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权移动到 s2
// println!("{s1}"); // 编译错误:s1 已经无效
s1 的栈数据(指针、长度、容量)被浅拷贝到 s2,但堆上的字符串没有复制。为避免 s1 和 s2 同时释放同一堆内存,Rust 使 s1 失效。这叫做移动。
2. 拷贝:Copy 类型
let x = 5;
let y = x; // x 的完整值被复制给 y,x 仍然有效
println!("x = {x}, y = {y}"); // 正常
i32 等实现了 Copy trait 的类型,赋值时直接复制栈上全部内容,成本极低,所以原变量仍有效。
常见 Copy 类型:整数、浮点、bool、char,以及元素全为 Copy 的元组。
3. 克隆:显式深拷贝
let s1 = String::from("hello");
let s2 = s1.clone(); // 堆上的 "hello" 也被完整复制一份
println!("s1 = {s1}, s2 = {s2}"); // 两者都有效
clone() 会复制堆数据,成本较高,需要显式调用。
四、变量重新赋值:旧值立即释放
let mut s = String::from("hello");
s = String::from("ahoy"); // 旧的 "hello" 立刻被 drop,内存释放
println!("{s}, world!"); // "ahoy, world!"
当给已有变量赋值新值时,旧值自动被 drop,不会内存泄漏。
五、所有权与函数
1. 传参:移动或复制
fn main() {
let s = String::from("hello");
takes_ownership(s); // s 的所有权移入函数,此后 s 无效
let x = 5;
makes_copy(x); // x 是 Copy 类型,只是复制传参,x 仍有效
println!("{x}"); // 正常
}
fn takes_ownership(some_string: String) {
println!("{some_string}");
} // some_string 离开作用域,drop 被调用
fn makes_copy(some_integer: i32) {
println!("{some_integer}");
}
2. 返回值:转移所有权出函数
fn main() {
let s1 = gives_ownership(); // 所有权从函数移到 s1
let s2 = String::from("hello");
let s3 = takes_and_gives_back(s2); // s2 移入,再移出给 s3
} // s1、s3 离开作用域,被释放;s2 已被移走,无事发生
fn gives_ownership() -> String {
let some_string = String::from("yours");
some_string
}
fn takes_and_gives_back(a_string: String) -> String {
a_string
}
3. 不方便时:用元组把所有权还回去
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{s2}' is {len}.");
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len();
(s, length) // 把原字符串和长度一起返回
}
但这样很啰嗦。于是 Rust 提供了引用(references) ,允许不获取所有权就使用值,下一章会讲到。
六、核心设计哲学
- Rust 绝不自动进行深拷贝,所有自动复制都是低开销的(要么栈数据移动,要么 Copy 类型的位拷贝)。
- RAII:资源获取即初始化,变量离开作用域时自动释放资源,避免忘记释放或重复释放。
- 所有权机制在编译期消除内存错误,运行时不引入任何额外开销。
掌握了这些,你就理解了 Rust 管理内存的根本方式。一切后续的引用、借用、生命周期都建立在所有权之上。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 emoeem!
评论



