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 账号登录后即可参与讨论