Skip to content

ownership.rs

文件信息

  • 📄 原文件:01_ownership.rs
  • 🔤 语言:rust

完整代码

rust
// ============================================================
//                      所有权(Ownership)
// ============================================================
// 所有权是 Rust 最独特的核心概念,也是 Rust 无需垃圾回收就能
// 保证内存安全的关键机制
//
// 三条所有权规则:
// 1. 每个值都有一个所有者(owner)
// 2. 同一时刻只能有一个所有者
// 3. 当所有者离开作用域时,值被自动释放(drop)

fn main() {
    println!("=== 所有权 ===");

    // ----------------------------------------------------------
    // 1. 作用域与所有权
    // ----------------------------------------------------------
    // 变量在声明时获得所有权,离开作用域时自动释放
    // 【类比】类似 C++ 的 RAII(Resource Acquisition Is Initialization)

    {
        let s = String::from("hello");  // s 获得 String 的所有权
        println!("作用域内: {}", s);
    } // s 离开作用域,String 被自动释放(调用 drop)
    // println!("{}", s);  // 错误!s 已不存在

    // ----------------------------------------------------------
    // 2. 移动(Move)
    // ----------------------------------------------------------
    // 对于堆上的数据(如 String),赋值操作会转移所有权
    // 【重要】这是"移动",不是"浅拷贝"——原变量失效
    // 【原因】防止两个变量同时释放同一块内存(double free)

    let s1 = String::from("hello");
    let s2 = s1;  // s1 的所有权移动到 s2
    // println!("{}", s1);  // 错误!s1 已失效(value used after move)
    println!("移动后: s2 = {}", s2);

    // 【对比】栈上数据(如 i32)是复制,不是移动
    let x = 5;
    let y = x;  // x 被复制到 y(i32 实现了 Copy trait)
    println!("复制: x={}, y={}", x, y);  // 两个都可以用

    // 【规则】实现了 Copy trait 的类型赋值时复制:
    // - 所有整数、浮点、布尔、字符类型
    // - 元素全部是 Copy 的元组(如 (i32, f64))
    // - 不可变引用 &T
    // 【不是 Copy 的】String, Vec, Box, 等堆上数据

    // ----------------------------------------------------------
    // 3. 克隆(Clone)
    // ----------------------------------------------------------
    // 如果确实需要深拷贝,使用 .clone()
    // 【注意】clone() 可能很昂贵(拷贝堆数据),要有意识地使用

    let s1 = String::from("hello");
    let s2 = s1.clone();  // 深拷贝
    println!("克隆: s1={}, s2={}", s1, s2);  // 两个都有效

    // ----------------------------------------------------------
    // 4. 函数与所有权
    // ----------------------------------------------------------
    // 传参和赋值一样:堆数据移动,栈数据复制
    // 返回值也会转移所有权

    let s = String::from("你好");
    takes_ownership(s);  // s 的所有权移入函数
    // println!("{}", s);  // 错误!s 已失效

    let x = 42;
    makes_copy(x);  // x 被复制,不影响原值
    println!("函数后 x 仍然可用: {}", x);

    // 返回值转移所有权
    let s1 = gives_ownership();           // 函数返回值移给 s1
    let s2 = String::from("world");
    let s3 = takes_and_gives_back(s2);   // s2 移入,返回值移给 s3
    println!("s1={}, s3={}", s1, s3);
    // println!("{}", s2);  // 错误!s2 已移入函数

    // ----------------------------------------------------------
    // 5. 所有权与效率
    // ----------------------------------------------------------
    // 如果每次传参都移动所有权,用完还要还回来,太麻烦了
    // 这就是为什么 Rust 有"借用"机制(见下一节)
    //
    // 不好的写法(反复移动):
    let s = String::from("hello");
    let (s, len) = calculate_length_bad(s);  // 必须还回来
    println!("长度={}, 字符串={}", len, s);

    // 好的写法(使用引用,见 02_borrowing.rs):
    // let len = calculate_length(&s);  // 借用,不移动

    // ----------------------------------------------------------
    // 6. 栈与堆
    // ----------------------------------------------------------
    // 理解所有权需要理解栈和堆的区别
    //
    // 栈(Stack):
    //   - 大小固定的数据:i32, f64, bool, char, [T; N], 元组
    //   - 分配/释放极快(移动栈指针)
    //   - 赋值时直接复制
    //
    // 堆(Heap):
    //   - 大小可变的数据:String, Vec, HashMap, Box
    //   - 分配需要找空间,释放需要归还
    //   - 赋值时移动所有权(避免拷贝开销)
    //
    // String 在内存中的布局:
    //
    //  栈上                堆上
    //  ┌─────────┐        ┌───┬───┬───┬───┬───┐
    //  │ ptr ─────┼───────>│ h │ e │ l │ l │ o │
    //  │ len: 5   │        └───┴───┴───┴───┴───┘
    //  │ cap: 5   │
    //  └─────────┘

    println!("\n=== 所有权结束 ===");
}

// ----------------------------------------------------------
// 获取所有权的函数
// ----------------------------------------------------------
fn takes_ownership(s: String) {
    println!("获取所有权: {}", s);
} // s 在此被释放

// ----------------------------------------------------------
// 复制值的函数
// ----------------------------------------------------------
fn makes_copy(x: i32) {
    println!("复制值: {}", x);
} // x 是复制的,不影响原值

// ----------------------------------------------------------
// 返回所有权
// ----------------------------------------------------------
fn gives_ownership() -> String {
    String::from("新字符串")  // 返回值的所有权移给调用者
}

fn takes_and_gives_back(s: String) -> String {
    s  // 直接返回,所有权转移给调用者
}

// ----------------------------------------------------------
// 反面示例:移动再归还(不推荐)
// ----------------------------------------------------------
fn calculate_length_bad(s: String) -> (String, usize) {
    let length = s.len();
    (s, length)  // 必须把 s 还回去,否则调用者就丢失了数据
}

💬 讨论

使用 GitHub 账号登录后即可参与讨论

基于 MIT 许可发布