Rust 学习笔记-所有权与借用(二)

14 阅读3分钟

Rust 学习笔记 - 所有权与借用

1. 所有权(Ownership)

所有权是Rust中最独特的概念之一,也是Rust在内存管理方面的核心机制。有别于其他编程语言(如Java、C#),Rust没有采用垃圾回收(Garbage Collection, GC)来管理内存,而是通过所有权系统来确保内存的安全使用。

Rust的所有权规则可以总结为以下几点:

  • 每个值都有一个所有者(Owner):在Rust中,每个数据值都有且只有一个变量作为它的所有者。
  • 一次只能有一个所有者:在某个时间点,一个值只能有一个所有者。所有权在变量之间的转移(move)过程中会发生变化,但不会同时存在多个所有者。
  • 所有者在离开作用域时,值将被自动清理:当一个变量离开它的作用域时,它所拥有的值会被释放,Rust会自动调用drop函数来释放相关的内存资源。

例如,以下代码展示了所有权的转移(move):

fn main() {
    let s1 = String::from("Hello");
    let s2 = s1;  // 所有权转移,s1不再有效
    println!("{}", s2);  // 可以正常访问s2
    // println!("{}", s1);  // 错误:s1已经失效
}

在这个例子中,当s2接管了s1的所有权后,s1不再有效,无法再访问。

2. 借用(Borrowing)

虽然Rust中一次只能有一个所有者,但为了避免频繁的所有权转移导致程序复杂性增加,Rust引入了借用(Borrowing)的概念。借用允许你在不转移所有权的情况下访问数据。

借用分为两种:

  • 不可变借用(Immutable Borrow):允许一个或多个借用者以只读方式访问数据,但不能修改它。通过引用(&)实现。

    fn main() {
        let s1 = String::from("Hello");
        let len = calculate_length(&s1);
        println!("The length of '{}' is {}.", s1, len);
    }
    
    fn calculate_length(s: &String) -> usize {
        s.len()  // 只读访问,不改变s1的所有权
    }
    
  • 可变借用(Mutable Borrow):允许一个可变借用者修改数据,但在同一时间只能有一个可变借用。通过可变引用(&mut)实现。

    fn main() {
        let mut s = String::from("Hello");
        change(&mut s);
        println!("{}", s);
    }
    
    fn change(some_string: &mut String) {
        some_string.push_str(", world!");
    }
    

可变借用的规则确保在同一时间点,只有一个地方能够修改数据,从而避免数据竞争和潜在的不安全行为。

3. 借用检查器(Borrow Checker)

Rust的编译器内置了一个借用检查器(Borrow Checker),它在编译时会检查你的代码是否遵循了所有权和借用的规则。借用检查器通过静态分析来确保内存安全,防止数据竞争、悬垂指针等问题。

4. 生命周期(Lifetimes)

在借用中,还需要考虑生命周期(Lifetimes)的问题。生命周期标注让编译器了解引用的有效范围,从而保证引用在使用时是有效的。

生命周期通常由编译器推导,但在一些复杂的借用场景中,可能需要显式标注生命周期。

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

这里的'a表示输入参数和返回值的生命周期是相关联的,即在函数返回时,返回值的生命周期不能超出输入参数的生命周期。