引言
所有权(Ownership)是Rust最独特和最重要的特性,它使Rust能够在没有垃圾回收器的情况下保证内存安全。今天我们将深入学习Rust的所有权系统。
一、什么是所有权?
1.1 所有权规则
Rust的所有权系统有三条核心规则:
- Rust中的每个值都有一个被称为其**所有者(owner)**的变量
- 值在任何时刻只能有一个所有者
- 当所有者离开作用域时,值将被丢弃(dropped)
1.2 作用域
            
            
              rust
              
              
            
          
          fn main() {
    {                      // s不可用,尚未声明
        let s = "hello";   // 从此处开始,s可用
        println!("{}", s); // 使用s
    }                      // s的作用域结束,s不再可用
    
    // println!("{}", s);  // ❌ 错误:s已离开作用域
}二、String类型与所有权
2.1 String vs 字符串字面量
            
            
              rust
              
              
            
          
          fn main() {
    // 字符串字面量:不可变,固定大小
    let s1 = "hello";
    
    // String类型:可变,大小可变
    let mut s2 = String::from("hello");
    s2.push_str(", world!");
    println!("{}", s2);
}2.2 内存分配
            
            
              rust
              
              
            
          
          fn main() {
    let s = String::from("hello");
    // s在堆上分配内存
    // 当s离开作用域时,Rust自动调用drop函数释放内存
} // s在这里被drop三、移动(Move)
3.1 基本类型的复制
            
            
              rust
              
              
            
          
          fn main() {
    let x = 5;
    let y = x;  // 复制
    
    println!("x = {}, y = {}", x, y);  // ✅ 都可用
}3.2 String的移动
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // 移动!s1不再有效
    
    // println!("{}", s1);  // ❌ 错误:s1已被移动
    println!("{}", s2);     // ✅ s2可用
}为什么?
- String由三部分组成:指针、长度、容量
- 赋值时只复制这三个值(栈上数据)
- 为避免二次释放,s1变为无效
3.3 移动语义
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = String::from("hello");
    
    let s2 = s1;  // s1的所有权移动到s2
    
    // s1不再有效
    // let s3 = s1;  // ❌ 错误:s1已被移动
    
    let s3 = s2;  // s2的所有权移动到s3
    println!("{}", s3);
}四、克隆(Clone)
4.1 深拷贝
如果确实需要深拷贝堆上的数据:
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = String::from("hello");
    let s2 = s1.clone();  // 深拷贝
    
    println!("s1 = {}, s2 = {}", s1, s2);  // ✅ 都可用
}注意:clone操作可能很耗时,因为它会复制堆上的数据。
4.2 Copy trait
某些类型实现了Copy trait,赋值时会自动复制:
            
            
              rust
              
              
            
          
          fn main() {
    // 这些类型都实现了Copy
    let x = 5;           // i32
    let y = x;
    
    let a = true;        // bool
    let b = a;
    
    let c = 'z';         // char
    let d = c;
    
    let e = (1, 2);      // 元组(所有元素都是Copy)
    let f = e;
    
    // 都可用
    println!("x={}, y={}", x, y);
    println!("a={}, b={}", a, b);
}实现Copy的类型:
- 所有整数类型
- 布尔类型
- 所有浮点类型
- 字符类型
- 元组(仅当其所有字段都是Copy)
五、所有权与函数
5.1 传递参数
            
            
              rust
              
              
            
          
          fn main() {
    let s = String::from("hello");
    
    takes_ownership(s);  // s的所有权移动到函数
    
    // println!("{}", s);  // ❌ 错误:s已被移动
    
    let x = 5;
    makes_copy(x);  // x是Copy类型,会复制
    
    println!("x = {}", x);  // ✅ x仍然可用
}
fn takes_ownership(some_string: String) {
    println!("{}", some_string);
} // some_string在这里被drop
fn makes_copy(some_integer: i32) {
    println!("{}", some_integer);
}5.2 返回值
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = gives_ownership();  // 函数返回值的所有权转移给s1
    println!("{}", s1);
    
    let s2 = String::from("hello");
    let s3 = takes_and_gives_back(s2);  // s2移入,返回值移给s3
    
    // println!("{}", s2);  // ❌ s2已被移动
    println!("{}", s3);     // ✅ s3可用
}
fn gives_ownership() -> String {
    let some_string = String::from("yours");
    some_string  // 返回,所有权移出
}
fn takes_and_gives_back(a_string: String) -> String {
    a_string  // 返回,所有权移出
}5.3 返回多个值
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = String::from("hello");
    
    let (s2, len) = calculate_length(s1);
    
    println!("字符串'{}' 的长度是 {}", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
    let length = s.len();
    (s, length)  // 返回String和长度
}六、实战示例
示例1:交换变量
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = String::from("hello");
    let s2 = String::from("world");
    
    println!("交换前: s1={}, s2={}", s1, s2);
    
    let (s1, s2) = swap(s1, s2);
    
    println!("交换后: s1={}, s2={}", s1, s2);
}
fn swap(a: String, b: String) -> (String, String) {
    (b, a)
}示例2:字符串处理
            
            
              rust
              
              
            
          
          fn main() {
    let s = String::from("hello");
    let s = add_exclamation(s);
    println!("{}", s);
}
fn add_exclamation(mut s: String) -> String {
    s.push('!');
    s  // 返回所有权
}示例3:构建器模式
            
            
              rust
              
              
            
          
          struct Config {
    host: String,
    port: u16,
}
impl Config {
    fn new() -> Self {
        Config {
            host: String::from("localhost"),
            port: 8080,
        }
    }
    
    fn host(mut self, host: String) -> Self {
        self.host = host;
        self  // 返回self,允许链式调用
    }
    
    fn port(mut self, port: u16) -> Self {
        self.port = port;
        self
    }
}
fn main() {
    let config = Config::new()
        .host(String::from("127.0.0.1"))
        .port(3000);
    
    println!("{}:{}", config.host, config.port);
}七、所有权的好处
7.1 内存安全
            
            
              rust
              
              
            
          
          fn main() {
    let s = String::from("hello");
    
    // Rust在编译时防止以下问题:
    // 1. 空指针
    // 2. 悬垂指针
    // 3. 双重释放
    // 4. 内存泄漏
}7.2 无需垃圾回收
            
            
              rust
              
              
            
          
          fn main() {
    let s = String::from("hello");
    // 编译时就知道何时释放内存
    // 无需运行时垃圾回收
    // 性能可预测
}八、常见模式
8.1 临时所有权转移
            
            
              rust
              
              
            
          
          fn main() {
    let s = String::from("hello");
    let len = calculate_length_ownership(s);
    
    // ❌ s已不可用
    // println!("{}", s);
}
fn calculate_length_ownership(s: String) -> usize {
    s.len()
}  // s被drop8.2 使用引用(预告)
            
            
              rust
              
              
            
          
          fn main() {
    let s = String::from("hello");
    let len = calculate_length_ref(&s);  // 传递引用
    
    println!("字符串'{}'的长度是{}", s, len);  // ✅ s仍可用
}
fn calculate_length_ref(s: &String) -> usize {
    s.len()
}  // s(引用)离开作用域,但不会drop原值九、练习题
练习1:修复所有权错误
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1移动到s2
    
    // 方案1:使用s2
    println!("{}", s2);
    
    // 方案2:克隆s1
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("{} {}", s1, s2);
    
    // 方案3:使用引用(下一篇)
}练习2:函数所有权
            
            
              rust
              
              
            
          
          fn main() {
    let s = String::from("hello");
    process(s);
    
    // 如果后续还需要使用s:
    // 方案1:返回所有权
    let s = String::from("hello");
    let s = process_and_return(s);
    println!("{}", s);
    
    // 方案2:使用引用(下一篇)
}
fn process(s: String) {
    println!("{}", s);
}
fn process_and_return(s: String) -> String {
    println!("{}", s);
    s
}练习3:字符串拼接
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
    
    // 方式1:s1会被移动
    let s3 = s1 + &s2;
    println!("{}", s3);
    // println!("{}", s1);  // ❌ s1已移动
    println!("{}", s2);     // ✅ s2可用
    
    // 方式2:使用format!(不移动)
    let s1 = String::from("Hello, ");
    let s2 = String::from("world!");
    let s3 = format!("{}{}", s1, s2);
    println!("{} {} {}", s1, s2, s3);  // 都可用
}十、所有权流程图
创建值
  ↓
值拥有所有者
  ↓
┌───────────────┐
│ 所有权操作?  │
└───────────────┘
  ↓         ↓
移动       克隆
  ↓         ↓
新所有者   两个所有者
  ↓         ↓
离开作用域  各自离开作用域
  ↓         ↓
值被drop    各自被drop十一、与其他语言对比
C++
            
            
              cpp
              
              
            
          
          // C++:需要手动管理内存
std::string* s = new std::string("hello");
delete s;  // 必须手动释放Java
            
            
              java
              
              
            
          
          // Java:垃圾回收器自动管理
String s = new String("hello");
// 不确定何时回收,有性能开销Rust
            
            
              rust
              
              
            
          
          // Rust:编译时确定,零开销
let s = String::from("hello");
// 离开作用域自动释放,无运行时开销十二、常见错误
错误1:使用已移动的值
            
            
              rust
              
              
            
          
          fn main() {
    let s1 = String::from("hello");
    let s2 = s1;
    
    // ❌ 错误
    // println!("{}", s1);
    
    // ✅ 正确
    println!("{}", s2);
}错误2:返回局部变量的引用
            
            
              rust
              
              
            
          
          // ❌ 错误:返回悬垂引用
// fn dangle() -> &String {
//     let s = String::from("hello");
//     &s
// }  // s被drop,返回的引用无效
// ✅ 正确:返回所有权
fn no_dangle() -> String {
    let s = String::from("hello");
    s
}总结
本文学习了:
✅ 所有权的三条规则
✅ 移动(Move)语义
✅ 克隆(Clone)操作
✅ Copy trait
✅ 所有权与函数
核心要点:
- 每个值有且仅有一个所有者
- 赋值或传参会移动所有权
- Copy类型会复制,不移动
- 离开作用域时自动释放内存