rust - 如何在不将可变引用传递给函数的情况下重新借用它?

标签 rust borrow-checker

我发现了一个案例,手动内联一个函数会改变借用检查器对待它的方式,以至于它不再编译。据推测,它依赖于函数签名中的信息。我如何在内联版本中提供此信息?

我认为它是如何工作的

'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 绑定(bind)以使编译器推断出与以前版本相同的类型?

其他一些尝试

这是另一个有效的版本,但没有定义名称 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结合所有四个版本。

最佳答案

正如人们所期望的那样,显而易见的解决方案有效:

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: &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 可变引用不能复制甚至克隆,因为它会违反支撑 Rust 安全性的别名 XOR 可变性原则.

关于rust - 如何在不将可变引用传递给函数的情况下重新借用它?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43036156/

相关文章:

rust - 有没有类似try_retain()的东西?

rust - 为什么不能在同一结构中存储值和对该值的引用?

rust - 一生可能活得不够长

closures - filter(|x|) 和 filter(|&x|) 有什么区别?

rust - 与(表面上)似乎完全安全的短暂生命周期值相混淆

rust - 为什么我们必须借用变量的类型而不是名称

rust - 调用返回引用的函数时了解非词法生命周期

rust - 与可变借用相关的不可变借用导致 "cannot borrow ` *self` 一次多次可变”

rust - 如何在可以是整数或 float 的泛型类型上调用 `min`?

rust - 惯用地访问可变和不可变向量的元素