自学内容网 自学内容网

Rust 中的引用与借用:深入理解所有权与数据安全

一、引用:不拥有数据的指针

引用本质上是一个指针,它指向一个变量的值,但不拥有该值。通过引用,我们可以在不转移所有权的情况下访问数据。

以下代码展示了如何通过引用避免转移所有权:

fn calculate_length(s: &String) -> usize {
    s.len()
}

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.", s1, len);
}

关键点

  1. 在调用 calculate_length 时,我们传递了 &s1,即变量 s1 的引用。
  2. 函数参数类型为 &String,表明它接受一个引用。
  3. 引用不拥有数据,因此在函数调用后,s1 依然有效,可以继续使用。

这种机制避免了在函数间频繁地转移所有权,也减少了需要返回数据的情况。

二、借用:像借东西一样使用数据

Rust 中创建引用的行为被称为 借用。这种设计与现实生活中的借用类似:借用者可以使用所有者的数据,但不能随意修改或销毁它。

借用分为两种:

  • 不可变引用:允许读取数据,但不能修改。
  • 可变引用:允许修改数据,但同时存在一些限制。
三、可变引用:对数据的受控修改

默认情况下,Rust 中的变量和引用是不可变的。如果需要修改借用的数据,可以使用 可变引用

以下代码展示了如何使用可变引用:

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

fn main() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);
}

变化点

  1. 使用 &mut 创建可变引用。
  2. 函数签名明确表明接受一个可变引用:some_string: &mut String
  3. 可变引用允许我们修改借用的数据。
四、可变引用的限制

Rust 对可变引用有严格的规则,主要目的是避免 数据竞争(data race):

  1. 同一时间只能存在一个可变引用

    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s; // 错误:不能同时有两个可变引用
    
  2. 不可变引用和可变引用不能同时存在

    let mut s = String::from("hello");
    let r1 = &s;
    let r2 = &mut s; // 错误:不可变引用和可变引用不能共存
    

这些限制确保了数据的修改是受控的,从而避免了潜在的并发问题。

五、作用域的技巧:灵活管理引用

引用的作用域从创建时开始,一直到最后一次使用为止。我们可以通过使用块作用域({})来显式管理引用的生命周期,从而避免冲突。

let mut s = String::from("hello");

{
    let r1 = &mut s;
    r1.push_str(", world");
} // r1 的作用域在此结束

let r2 = &mut s; // 可以安全创建新的可变引用
六、防止悬垂引用(Dangling References)

在其他语言中,指针可能会指向已经释放的内存,导致 悬垂指针 问题。而 Rust 编译器通过严格的生命周期检查,杜绝了悬垂引用的出现。例如:

fn dangle() -> &String {
    let s = String::from("hello");
    &s // 错误:s 在函数结束后会被释放
}

编译器会提示此代码无效,因为函数返回了一个指向无效内存的引用。

正确的方式是直接返回数据本身:

fn no_dangle() -> String {
    let s = String::from("hello");
    s // 所有权转移,避免悬垂引用
}
七、总结:引用与借用的核心规则
  1. 同一时间只能有一个可变引用,或多个不可变引用
  2. 引用必须始终有效

Rust 的引用与借用机制确保了内存使用的安全性,同时提供了灵活性。虽然这些规则在初学时可能会显得繁琐,但它们在防止数据竞争和提升代码健壮性方面无可替代。

通过熟练掌握引用和借用,您将能够更加高效地使用 Rust,编写出安全、优雅的代码。

延伸阅读:下一步可以学习 Rust 中的切片(Slices),它是另一种常见的引用形式,用于更细粒度地操作数据。


原文地址:https://blog.csdn.net/weixin_43114209/article/details/145300656

免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!