一、所有权是什么?

所有权是 Rust 管理内存的一套编译时规则,它让 Rust 无需 GC 也能保证内存安全,且不损失性能。核心只有三条:

  1. Rust 中每个值都有且只有一个所有者(owner)。
  2. 同一时刻,值只能有一个所有者。
  3. 当所有者离开作用域,值会被自动丢弃(调用 drop 释放内存)。

二、栈与堆的区分

  • :存放编译时大小固定的数据(整数、浮点、boolchar 等),复制快,离开作用域时直接弹出。
  • :存放编译时大小未知或可变的数据(如 String),需要分配器动态分配,访问需通过指针,稍慢。

所有权主要管理的是堆上的数据


三、移动(move)与拷贝(copy)

1. 移动:String 的赋值

let s1 = String::from("hello");
let s2 = s1;            // s1 的所有权移动到 s2
// println!("{s1}");   // 编译错误:s1 已经无效

s1 的栈数据(指针、长度、容量)被浅拷贝到 s2,但堆上的字符串没有复制。为避免 s1s2 同时释放同一堆内存,Rust 使 s1 失效。这叫做移动

2. 拷贝:Copy 类型

let x = 5;
let y = x;   // x 的完整值被复制给 y,x 仍然有效
println!("x = {x}, y = {y}"); // 正常

i32 等实现了 Copy trait 的类型,赋值时直接复制栈上全部内容,成本极低,所以原变量仍有效。

常见 Copy 类型:整数、浮点、boolchar,以及元素全为 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 管理内存的根本方式。一切后续的引用、借用、生命周期都建立在所有权之上。