【问题标题】:Why doesn't a mutable borrow of self change to immutable?为什么不可变的 self 借用变为不可变的?
【发布时间】:2017-05-02 14:25:06
【问题描述】:

此代码无法通过可怕的借用检查器 (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() 返回时会转换为不可变借用,因为该函数仅返回不可变引用。借阅检查器还不够聪明吗?这是计划好的吗?有办法解决吗?

【问题讨论】:

  • Limits of Lifetimes。这完全一样。
  • @Stargateur:我不认为“去糖”代码是为了编译;这只是为了说明。
  • @Stargateur:不,它没有帮助。需要限制loanplay.rust-lang.org/…的生命周期
  • @aSpex:是的,看起来确实是一样的。但是文档没有提出解决方案或解决方法?
  • 哦,确实“根据我们真正关心的引用语义,这个程序显然是正确的,但是生命周期系统太粗粒度,无法处理。”。所以唯一的解决方案是使用独立的作用域。 @aSpex 你应该发布一个答案。

标签: rust borrow-checker


【解决方案1】:

生命周期与引用是否可变是分开的。处理代码:

fn reference_to_a(&mut self) -> &i32

虽然已经省略了生命周期,但这相当于:

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

即输入和输出寿命相同。这是为这样的函数分配生命周期的唯一方法(除非它返回对全局数据的 &amp;'static 引用),因为您无法从零开始构建输出生命周期。

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

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

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

【讨论】:

  • 谢谢,有没有解决方案或变通方法?非词法生命周期会修复它吗?
  • 我认为除了将突变拆分为单独的方法调用之外没有其他解决方法。这不是一个词汇终身的事情。 &amp;i32 是从 &amp;mut self 借来的,所以它们基本上是联系在一起的。
  • @Timmmm OP 示例在启用 NLL 的情况下按原样工作。
  • “示例在启用 NLL 的情况下按原样工作” 只是因为 aref 未使用。如果你使用它,比如println!("{} {}", aref, dat.get_b());,那么 NLL 不会有任何帮助,正如@ChrisEmerson 解释的那样。
  • 是否有理由不能扩展 rust 以允许这样做,或者甚至为什么很难允许这样做。例如,为什么 rust 在返回时不能将可变的 self 借用“降级”为不可变的 self 借用。这样一来,它就可以工作(因为您可以对同一事物进行多次不可变的借用)。
【解决方案2】:

为什么这是一个错误:虽然@Chris 在大约 2.5 年前已经给出了更准确的解释,但您可以将 fn reference_to_a(&amp;mut self) -&gt; &amp;i32 视为声明:

“我想独占借用self,然后返回一个共享/不可变引用,该引用与原始独占借用一样长”(source)

显然是it can even prevent me from shooting myself in the foot

借用检查器还不够聪明吗?这是计划好的吗?

仍然无法表达“我想在调用期间专门借用 self,并返回具有独立生命周期的共享引用”。正如@aSpex 指出的那样,它是mentioned in the nomicon,截至2018 年底,它已被列入Things Rust doesn’t let you do

我找不到解决这个问题的具体计划,就像之前的 other borrow checker improvements were deemed higher priority。关于允许单独的读/写“终身角色”(Ref2&lt;'r, 'w&gt;) 的想法是mentioned in the NLL RFC,但据我所知,没有人将它变成自己的 RFC。

有没有办法解决它? 不是真的,但根据您首先需要它的原因,其他结构化代码的方法可能是合适的:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-05-17
    相关资源
    最近更新 更多