首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >01-Rust 教程 - 所有权

01-Rust 教程 - 所有权

作者头像
LarryLan
发布2026-03-30 18:07:06
发布2026-03-30 18:07:06
870
举报

Rust 教程 - 所有权

学 Rust 第一周:我是谁?我在哪?为什么我的代码又编译不过?


🎬 引入

说实话,刚开始学 Rust 的时候,我被所有权系统折磨得够呛。

记得那天晚上,我写了这样一段代码:

代码语言:javascript
复制
fn main() {
    let s = String::from("hello");
    let t = s;
    println!("{}", s);
}

我觉得很合理啊——把 s 赋值给 t,然后打印 s,有啥问题?

结果编译器给我泼了一盆冷水:

代码语言:javascript
复制
error[E0382]: borrow of moved value: `s`
 --> src/main.rs:4:20
  |
2 |     let s = String::from("hello");
3 |     let t = s;
  |         - value moved here
4 |     println!("{}", s);
  |                    ^ value borrowed here after move

我当时就想:这编译器是不是跟我过不去?

后来才明白,不是编译器有问题,是我没搞懂 Rust 的"规矩"。今天咱们就来搞懂这个让无数新手抓狂的所有权系统


📌 核心概念

所有权就三句话

Rust 的所有权系统,说白了就三条规则:

  1. 每个值都有一个主人(所有者)
  2. 同一时间只能有一个主人
  3. 主人离开作用域,值就被扔掉

听起来挺简单吧?但实际写代码的时候,坑可不少。


🏠 生活类比:房子与钥匙

所有权就像你家的房子:

🏠 房子只能有一个房主(同一时间只能有一个所有者) 🔑 但钥匙可以借给别人(可以借用,但不能据为己有) 🗑️ 房主搬走了,房子就被清空(所有者离开作用域,值被 drop)

这样一想,是不是好理解多了?


移动语义(Move)

在 Rust 里,赋值操作会转移所有权

代码语言:javascript
复制
let s1 = String::from("hello");
let s2 = s1;  // s1 的所有权转移给 s2

这时候 s1 还能用吗?不能!

代码语言:javascript
复制
println!("{}", s1);  // ❌ 编译错误!

编译器在说什么人话?

编译器:兄弟,s1 已经给别人了,你咋还用呢?


🎯 为什么这样设计?

其他语言(比如 Python、Java)怎么处理的?

代码语言:javascript
复制
# Python
s1 = "hello"
s2 = s1  # 两个变量都指向同一个字符串

Python 用的是引用计数,Rust 用的是所有权系统

Rust 的优势:

  • ✅ 编译期就保证内存安全
  • ✅ 不需要垃圾回收器
  • ✅ 性能更高

代价:

  • ❌ 编译器管得宽(但这是为你好!)

💻 代码示例

示例 1:所有权转移

代码语言:javascript
复制
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1 的所有权转移给 s2
    
    // println!("{}", s1);  // ❌ 错误!s1 已经无效了
    println!("{}", s2);  // ✅ 正确!s2 是新的所有者
}

运行结果:

代码语言:javascript
复制
hello

示例 2:克隆(Clone)

如果你想要一份副本,而不是转移所有权,怎么办?

.clone()

代码语言:javascript
复制
fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();  // 深拷贝,s1 和 s2 都有独立的数据
    
    println!("{}", s1);  // ✅ s1 还能用
    println!("{}", s2);  // ✅ s2 也能用
}

运行结果:

代码语言:javascript
复制
hello
hello

示例 3:复制(Copy)

有些类型实现了 Copy trait,赋值时自动复制:

代码语言:javascript
复制
fn main() {
    let x = ;  // i32 实现了 Copy
    let y = x;  // 自动复制,x 还能用
    
    println!("{}", x);  // ✅ 正确
    println!("{}", y);  // ✅ 正确
}

哪些类型实现了 Copy?

  • 整数类型(i32, u64 等)
  • 浮点类型(f32, f64
  • 布尔类型(bool
  • 字符类型(char
  • 元组(如果所有字段都实现了 Copy)

String 为什么没有 Copy?

因为 String 管理着堆上的数据,不能简单地按位复制!


🐛 常见坑点

坑点 1:函数传参转移所有权

代码语言:javascript
复制
fn takes_ownership(s: String) {
    println!("{}", s);
}  // s 在这里被 drop

fn main() {
    let s = String::from("hello");
    takes_ownership(s);  // s 的所有权转移给函数
    
    // println!("{}", s);  // ❌ 错误!s 已经无效了
}

编译器吐槽:

编译器:你把 s 传给函数后,它就不属于你了!

解决方案:

代码语言:javascript
复制
// 方案 1:传递引用(借用)
fn takes_reference(s: &String) {
    println!("{}", s);
}

// 方案 2:返回所有权
fn returns_ownership(s: String) -> String {
    s
}

坑点 2:函数返回值转移所有权

代码语言:javascript
复制
fn gives_ownership() -> String {
    String::from("hello")
}

fn main() {
    let s = gives_ownership();  // s 获得所有权
    println!("{}", s);  // ✅ 正确
}

这个没问题,但如果这样写:

代码语言:javascript
复制
fn wrong_way() -> String {
    let s = String::from("hello");
    s  // 返回 s,所有权转移给调用者
}

fn main() {
    let s = wrong_way();
    println!("{}", s);  // ✅ 正确
}

坑点 3:忘记 clone

代码语言:javascript
复制
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // 转移所有权
    
    println!("{}", s1);  // ❌ 错误!
}

错误信息:

代码语言:javascript
复制
error[E0382]: borrow of moved value: `s1`

修复方法:

代码语言:javascript
复制
let s2 = s1.clone();  // 克隆一份

🎯 实战案例

案例:处理用户输入

假设你要写一个函数,处理用户输入的字符串:

代码语言:javascript
复制
fn process_input(input: String) -> String {
    let processed = input.to_uppercase();
    processed  // 返回处理后的结果
}

fn main() {
    let user_input = String::from("hello");
    let result = process_input(user_input);
    
    // println!("{}", user_input);  // ❌ user_input 已经无效了
    println!("{}", result);  // ✅ 正确
}

如果你想保留原始输入怎么办?

代码语言:javascript
复制
fn process_input(input: &String) -> String {
    input.to_uppercase()  // 借用,不转移所有权
}

fn main() {
    let user_input = String::from("hello");
    let result = process_input(&user_input);  // 传递引用
    
    println!("{}", user_input);  // ✅ 还能用
    println!("{}", result);      // ✅ 正确
}

案例:多个变量共享数据

代码语言:javascript
复制
fn main() {
    let s1 = String::from("hello");
    let s2 = &s1;  // s2 借用 s1
    let s3 = &s1;  // s3 也借用 s1
    
    println!("{}", s1);  // ✅ 所有者还能用
    println!("{}", s2);  // ✅ 借用者也能用
    println!("{}", s3);  // ✅ 多个借用者
}

这就是借用的威力! 多个引用可以同时存在,只要它们都是不可变借用


🧠 思维导图

01-Rust 所有权
01-Rust 所有权

📝 小结

核心要点:

  1. 每个值都有一个所有者——这是 Rust 的"规矩"
  2. 赋值会转移所有权——除非类型实现了 Copy
  3. 需要副本用 clone()——深拷贝,但性能有开销
  4. 借用可以共享数据——&T 不转移所有权
  5. 编译器管得宽是为你好——编译期保证内存安全

金句:

Rust 的所有权系统就像个操心的老妈:"这个变量你拿好了吗?别人用了你还想用?不行!用完记得扔啊,别堆着!"


🔗 下篇预告

下一篇咱们讲讲变量与基本类型

  • 为什么 Rust 的变量默认不能改?
  • letconststatic 有啥区别?
  • Rust 有哪些基本类型?
  • 类型推断是怎么工作的?

敬请期待!


📚 参考资料

  • The Rust Book - Chapter 4: Ownership
  • Rust By Example
  • Rust 官方文档

💬 互动问题:

你学 Rust 时被所有权折磨过吗?在评论区分享你的踩坑经历吧!😄

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-03-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Larry的Hub 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Rust 教程 - 所有权
    • 🎬 引入
    • 📌 核心概念
      • 所有权就三句话
      • 🏠 生活类比:房子与钥匙
      • 移动语义(Move)
      • 🎯 为什么这样设计?
    • 💻 代码示例
      • 示例 1:所有权转移
      • 示例 2:克隆(Clone)
      • 示例 3:复制(Copy)
    • 🐛 常见坑点
      • 坑点 1:函数传参转移所有权
      • 坑点 2:函数返回值转移所有权
      • 坑点 3:忘记 clone
    • 🎯 实战案例
      • 案例:处理用户输入
      • 案例:多个变量共享数据
    • 🧠 思维导图
    • 📝 小结
    • 🔗 下篇预告
    • 📚 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档