为什么可变的自我借贷变成不可变的?

问题描述 投票:12回答:2

此代码使可怕的借阅检查器(playground)失败:

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());
}

由于实现了非词法生存期,因此触发该错误是必需的:

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

错误:

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

为什么?我本以为dat的可变借位在返回reference_to_a()时会转换为不可变的借位,因为该函数仅返回不可变的引用。借钱检查员还不够聪明吗?这有计划吗?有没有解决的办法?

rust borrow-checker
2个回答
5
投票

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

fn reference_to_a(&mut self) -> &i32

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

fn reference_to_a<'a>(&'a mut self) -> &'a i32

即输入和输出寿命是相同的。这是将生存期分配给这样的函数的唯一方法(除非它返回对全局数据的&'static引用),因为您不能从零开始弥补输出生存期。

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

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

正如@aSpex指出的,这是covered in the nomicon


1
投票

为什么会出错:尽管2.5年前@Chris已经给出了更精确的解释,但是您可以将fn reference_to_a(&mut self) -> &i32读为声明:

“我想排他地借用self,然后返回一个共享的/不可变的引用,该引用的生存期与原始排他期一样长”显然是source

借阅检查器还不够聪明吗?这有计划吗?

仍然无法表达“我想在通话期间专门借用我自己,并返回带有单独生命周期的共享引用”。正如@aSpex所指出的,它是it can even prevent me from shooting myself in the foot,并且在2018年末被列在mentioned in the nomicon中。

我无法像以前Things Rust doesn’t let you do那样找到解决这个问题的具体计划。关于允许单独的读/写“生命周期角色”(other borrow checker improvements were deemed higher priority)的想法是Ref2<'r, 'w>,但据我所知,没有人将其变成自己的RFC。

是否有解决方法?

并非如此,但是首先取决于您需要使用此代码的原因,可以使用其他结构化代码的方法:您可以返回副本/克隆而不是引用
    有时您可以将mentioned in the NLL RFC分成两部分,一个取fn(&mut self) -> &T,另一个取回&mut self&T
  • [在Rust中经常发生,重新排列结构as suggested by @Chris here会有所帮助
  • 您可以从方法中返回共享引用:to be "data-oriented" rather than "object-oriented"fn(&mut self) -> (&Self, &T)
  • 您可以使fn使用共享的from this answer引用并使用&self(即,将interior mutability的需要定义为SelfCell<T>的部分定义)。这可能看起来像作弊,但这实际上是适当的,例如当您需要可变性为RefCell<T>的原因时。毕竟,我们正在制定一种将implementation detail of a logically-immutable method &mut self设置为not because it mutates parts of self, but的方法。
  • © www.soinside.com 2019 - 2024. All rights reserved.