对自我的可变借用不会变为不可变

Tim*_*mmm 11 rust borrow-checker

这段代码无法通过可怕的借阅检查器(游乐场):

struct Data {
    a: i32,
    b: i32,
    c: i32,
}

impl Data {
    fn reference_to_a(&mut self) -> &i32 {
        self.c = 1;
        &self.a
    }
    fn get_b(&self) -> i32 {
        self.b
    }
}

fn main() {
    let mut dat = Data{ a: 1, b: 2, c: 3 };
    let aref = dat.reference_to_a();
    println!("{}", dat.get_b());
}
Run Code Online (Sandbox Code Playgroud)

错误:

error[E0502]: cannot borrow `dat` as immutable because it is also borrowed as mutable
  --> <anon>:19:20
   |
18 |     let aref = dat.reference_to_a();
   |                --- mutable borrow occurs here
19 |     println!("{}", dat.get_b());
   |                    ^^^ immutable borrow occurs here
20 | }
   | - mutable borrow ends here
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么这是?我原以为datreference_to_a()返回时可变借位被转换为不可变的,因为该函数只返回一个不可变引用.借用检查员还不够聪明吗?这是计划好的吗?有办法解决吗?

Chr*_*son 5

生存期与引用是否可变无关。通过代码工作:

fn reference_to_a(&mut self) -> &i32
Run Code Online (Sandbox Code Playgroud)

尽管生命周期已被消除,但这等效于:

fn reference_to_a<'a>(&'a mut self) -> &'a i32
Run Code Online (Sandbox Code Playgroud)

即输入和输出寿命是相同的。这是为这样的函数分配生存期的唯一方法(除非它返回了&'static对全局数据的引用),因为您无法一无所获地弥补输出生存期。

这意味着,如果通过将返回值保存在变量中来使其保持&mut self活动状态,那么您也将保持活动状态。

另一种思考方式是,&i32是的子借项&mut self,因此仅在到期之前有效。

正如@aSpex指出的那样,这在nomicon中进行了介绍

  • 除了将突变拆分为单独的方法调用之外,我认为没有其他解决方法。这不是一生的词汇。&i32是从&mut self借来的,因此它们从根本上联系在一起。 (3认同)
  • @Timmmm OP示例在启用NLL的情况下按原样工作。 (2认同)
  • _“示例在启用 NLL 的情况下按原样工作”_ 仅因为 `aref` 未使用。如果你使用它,比如 `println!("{} {}", aref, dat.get_b());` 那么 NLL 将没有任何帮助,正如@ChrisEmerson 所解释的那样。 (2认同)