【问题标题】:How can I reborrow a mutable reference without passing it to a function?如何在不将可变引用传递给函数的情况下借用可变引用?
【发布时间】:2017-03-27 00:23:45
【问题描述】:

我发现手动内联函数会改变借用检查器处理它的方式,使其不再编译。大概它依赖于函数签名中的信息。如何在内联版本中提供这些信息?

我认为它是如何工作的

'a'b 成为'a'b 短的生命周期(可以写成'b: 'a)。

假设我有一个p: &'b mut f32。我可以简单地借用p(和&mut p)得到q: &'a mut &'b mut f32

  1. 我是否正确理解&'a mut &'b mut f32 等同于&'a mut &'a mut f32 因为'b: 'a

然后我可以取消引用q(使用*q)来获得r: &'a mut f32。我可以通过r(使用*r = something)写信给f32,然后我可以稍后(在生命周期之外'a)通过p(使用*p)读回该值。

通过函数调用

这是我认为使用上述顺序的一些工作代码:

fn reborrow<'a, 'b: 'a>(q: &'a mut &'b mut f32) -> &'a mut f32 {
    *q
}

fn main() {
    let mut x: f32 = 3.142;
    let mut p = &mut x;
    {
        let q = &mut p;
        let r = reborrow(q);
        *r = 2.718;
    }
    assert_eq!(*p, 2.718);
}

(在reborrow() 的主体中将*q 替换为q 也可以,因为Rust 会在缺失时插入必要的取消引用)。

手动内联

如果我手动内联 reborrow() 调用,它将不再编译:

fn main() {
    let mut x: f32 = 3.142;
    let mut p = &mut x;
    {
        let q = &mut p;
        let r = *q; <-- ERROR REPORTED HERE.
        *r = 2.718;
    }
    assert_eq!(*p, 2.718);
}
error[E0507]: cannot move out of borrowed content
  1. 谁拿走了我的玩具?类型推理的想法/缺失是什么?

  2. 我能否以某种方式注释 let 绑定以使编译器推断与以前版本中相同的类型?

其他一些尝试

这是另一个有效的版本,但没有定义名称r

fn main() {
    let mut x: f32 = 3.142;
    let mut p = &mut x;
    {
        let q = &mut p;
        **q = 2.718;
    }
    assert_eq!(*p, 2.718);
}

这是一个解决方法,它定义了名称 r 并且有效,但不使用相同的借用和取消引用序列:

fn main() {
    let mut x: f32 = 3.142;
    let mut p = &mut x;
    {
        let q = &mut p;
        let r = &mut **q;
        *r = 2.718;
    }
    assert_eq!(*p, 2.718);
}

我做了一个playground 结合了所有四个版本。

【问题讨论】:

  • FWIW,根据您的 reborrow 方法,我有一个 follow-up question
  • 这可能会有所帮助:bluss.github.io/rust/fun/2015/10/11/…
  • @BurntSushi5 我也有类似的想法,但 {} 技巧在这里不起作用,这似乎表明它在某种程度上有所不同。
  • @BurntSushi5 不过很有趣。 :-)

标签: rust borrow-checker


【解决方案1】:

正如预期的那样,显而易见的解决方案有效:

fn main() {
    let mut x: f32 = 3.142;
    let mut p = &mut x;
    {
        let r: &mut f32 = p;
        *r = 2.718;
    }
    assert_eq!(*p, 2.718);
}

这似乎比较直观,是我希望新人最终会得到的。


但是,如果您开始考虑它,那将毫无意义。如上所述,它看起来像:

  • let r: &amp;mut f32 = p;p 移出
  • 但我们稍后在assert_eq!(*p, 2.718); 中使用p

一个合理的解释是pCopy,但它不是1

答案是,隐含地,Rust 在幕后执行重新借用。即显式代码为:

fn main() {
    let mut x: f32 = 3.142;
    let mut p = &mut x;
    {
        let r: &mut f32 = &mut *p;
        *r = 2.718;
    }
    assert_eq!(*p, 2.718);
}

我们可以通过重新借用后尝试读取p来检查这一点,并检查编译器错误:

error[E0502]: cannot borrow `p` as immutable because `*p` is also borrowed as mutable
 --> <anon>:6:24
  |
5 |         let r: &mut f32 = p;
  |                           - mutable borrow occurs here
6 |         println!("{}", p);
  |                        ^ immutable borrow occurs here
7 |         *r = 2.718;
8 |     }
  |     - mutable borrow ends here

error: aborting due to previous error

这证实了p 确实只是可变借用,而不是移动、克隆或复制。

1可变引用不能是 Copy 甚至是 Clone,因为它会违反支撑 Rust 安全性的别名异或可变性原则。

【讨论】:

  • 好吧,我想这至少证实了我在问题中写的内容。在这种情况下,您是否有类型推断如何工作/中断的模型?
  • @apt1002:对不起,没有。我明白会发生什么,但我不确定。如果变量被使用,它可能就像重新借用一样简单,否则移动,但可能存在我无法解释的边缘情况。
【解决方案2】:

我无法开始解释这一点,但您可以使用与隐式取消引用类似的技巧并说 r&amp;mut f32

fn main() {
    let mut x: f32 = 3.142;
    let mut p = &mut x;
    {
        let q = &mut p;
        let r: &mut f32 = q;
        *r = 2.718;
    }
    assert_eq!(*p, 2.718);
}

【讨论】:

  • 天哪,即使let r: &amp;mut f32 = *q; 也有效。很奇怪。我真的很期待一个解释!
猜你喜欢
  • 1970-01-01
  • 2021-05-10
  • 1970-01-01
  • 1970-01-01
  • 2023-01-02
  • 2018-11-10
  • 2019-12-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多