阶段二:所有权与引用

78 阅读2分钟

核心概念

  1. 所有权(Ownership)

    • 规则:
      • Rust中每个值有且只有一个所有者(Owner)
      • 当所有者离开作用域,值会被自动释放
      • 赋值操作(如let a = b)可能转移所有权(Move)而非复制
    • 示例:
      let s1 = String::from("hello"); // s1拥有字符串
      let s2 = s1;                    // 所有权转移给s2
      // println!("{}", s1);          // 错误!s1不再有效
      
  2. 借用(Borrowing)

    • 通过引用(&)借用数据,不获取所有权
    • 规则:
      • 同一时间,要么只能有一个可变引用(&mut),要么有多个不可变引用(&
      • 引用必须始终有效(悬垂引用禁止)
    • 示例:
      let mut s = String::from("hello");
      let r1 = &s;       // 不可变引用
      let r2 = &s;       // 允许第二个不可变引用
      // let r3 = &mut s; // 错误!已有不可变引用存在
      
  3. 生命周期(Lifetime)

    • 确保引用始终指向有效数据
    • 编译器会自动推断大多数情况,复杂场景需要手动标注

练习题

题目1:计算字符串长度的函数

fn calculate_length(s: &String) -> usize {
    s.len() // 返回长度,不获取所有权
}

fn main() {
    let s = String::from("ownership");
    let len = calculate_length(&s);
    println!("'{}'的长度是{}", s, len); // 正确:s仍有效
}

题目2:触发所有权错误

fn main() {
    let s1 = String::from("hello");
    let s2 = s1;            // 所有权转移
    println!("{}", s1);     // 故意触发错误
}
  • 编译后会看到错误信息:borrow of moved value: 's1'
  • 修复方法:使用克隆(let s2 = s1.clone();

题目3:结构体与方法

struct Person {
    name: String,
    age: u8,
}

impl Person {
    // 方法:不可变借用
    fn introduce(&self) {
        println!("我叫{},今年{}岁", self.name, self.age);
    }

    // 方法:可变借用修改年龄
    fn birthday(&mut self) {
        self.age += 1;
    }
}

fn main() {
    let mut bob = Person {
        name: String::from("Bob"),
        age: 30,
    };
    bob.introduce();    // 输出:我叫Bob,今年30岁
    bob.birthday();     // 修改年龄
    bob.introduce();    // 输出:我叫Bob,今年31岁
}

常见问题解决

  1. 错误:cannot borrow 'x' as mutable more than once at a time

    • 原因:同时存在多个可变引用
    • 修复:限制作用域
      let mut s = String::from("hello");
      {
          let r1 = &mut s;
      } // r1离开作用域
      let r2 = &mut s; // 允许
      
  2. 错误:missing lifetime specifier

    • 原因:结构体包含引用但未指定生命周期
    • 修复(后续阶段会深入):
      struct Book<'a> {
          title: &'a str, // 显式标注生命周期
      }