可变与不可变引用浅析

一开始接触可变引用与不可变引用的时候,我们都觉得自己很快能理解。

let mut s = String::from("hello");
let r1 = &mut s; // r1 可变引用
let r2 = &s; // r2 不可变引用

从上面的形式我们很容易理解。接下来我们从书上看到多个不可变引用在同一个作用域内是可以正常执编译通过的,如同我们把一份文档导出成了多个不可编辑的pdf,分发给朋友,每个朋友只能看,但是无法修改,因此复制多少份都没关系。而且所有权还在我们自己手上。

let mut s = String::from("hello");
let r1 = &s; // r1 不可变引用
let r2 = &s; // r2 不可变引用

那么如果出现多个声明的可变借用会出现什么现象呢?我们先来看看官方书中的原话:

可变引用在使用上有一个很大的限制:对于特定作用域中的特 定数据来说,一次只能声明一个可变引用。以下代码尝试违背这一限 制,则会导致编译错误:

{
  let mut s = String::from("hello");
  let r1 = &mut s;
  let r2 = &mut s;
}
$ error[E0499]: cannot borrow `s` as mutable more than once at a time
 --> borrow_twice.rs:5:19

此时你看到这句话是来自官方书籍,你就信了吗?不妨编译运行下看看结果吧

    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.90s
     Running `target/debug/examples/ownership`
➜  practice git:(master)

发现没有,直接编译通过。此时你很费解,不是说好了不能同时出现2次可变声明吗?
这里面就不得不谈到rust的生命周期和作用域在变量上的作用关系了。

  • 生命期和作用域的关系:
    • 作用域指的是你在代码中看到的大括号 {} 内的范围。
    • 生命周期是 Rust 编译器跟踪引用何时有效的时间段。

我们来继续解释上面的代码为什么能直接编译通过。
其实这里面的r1后面并没有进一步使用,也就是它的生命周期在r2声明的时候就已经完结了。接下来你声明r2的时候,r2会认为目前就只有它自己是唯一的可变借用,因此不会发生编译冲突。

好,你以为你已经都明白了吧,我们还经常会看到这样一句话「同一作用域内,可变引用与不可变引用不能共存」,也就是说下面这段代码也不应该编译通过才对:

fn main() {
    let mut x = 5;

    let y = &x;    // 不可变引用
    println!("{}", y);
    let z = &mut x; // 可变引用
    println!("{}", z);
    let y2 = &x;   // 再次创建不可变引用
    println!("{}", y2);
}

但是利用刚刚我们已经谈过的内容,你会马上恍然大悟,他们虽然在同一个作用域,但是在作用域内没有交替使用它们,也就是它们的生命周期不重叠。
具体来说,Rust 的借用检查器在处理这些引用时会按照它们的生命周期进行分析:

  1. y 是不可变引用,在 println! 打印后,它的生命周期结束。
  2. z 是可变引用,在 println! 打印后,它的生命周期开始,并且不会与 y 冲突,因为 y 已经结束。
  3. 然后,y2 被创建,并且由于 z 的作用域也已经结束,y2 可以安全地作为不可变引用存在。

最后,我们再看一段代码:

fn main() {
    let mut x = 5;

    let y = &x;  
    println!("{}", y);
    let z = &mut x; 
    println!("{}", z);
    let y2 = &x;
    println!("{}", z);
}

发现没有,可变引用与不可变引用交替声明了,同时还交替使用了,因此会编译出错:

 --> examples/ownership.rs:8:14
  |
6 |     let z = &mut x; // 可变引用,错误:不允许在有不可变引用的情况下创建可变引用
  |             ------ mutable borrow occurs here
7 |     println!("{}", z);
8 |     let y2 = &x;
  |              ^^ immutable borrow occurs here
9 |     println!("{}", z);
  |                    - mutable borrow later used here

结论:

  • 同一作用域下,不可变引用和可变引用都可以可以有N多个
  • 同一作用域下,不可变引用和可变引用的声明可以同时存在
  • 同一作用域下,不可变引用和可变引用可以交替声明
  • 同一作用域下,不可变引用和可变引用不可以交替使用
本作品采用《CC 协议》,转载必须注明作者和本文链接
努力是不会骗人的!
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!