如何将可变引用重新导入而不将其传递给函数?

时间:2017-03-27 00:23:46

标签: 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
  2. 然后,我可以取消引用q(使用*q)来获取r: &'a mut f32。我可以通过f32r)写*r = something,我可以稍后(生命期外'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); } )。

    使用函数调用

    以下是一些我认为使用上述序列的工作代码:

    *q

    (在q正文中用reborrow()替换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
    
    let
    1. 谁带走了我的玩具?什么是类型推理思考/缺失?

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

    3. 其他一些尝试

      这是另一个有效的版本,但没有定义名称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); } 并且有效的解决方法,但不使用相同的借用和解除引用序列:

      $('.toggle-menu').click(function (e) {
          e.preventDefault();
          $('h4.toggle-menu').text($(this).text() == 'Menu' ? 'Close' : 'Menu');
         
          $('.circle').toggleClass('Opacity');
          $('#overlay-menu').delay(5000).toggleClass('Opacity');
          $('.circle').toggleClass('open');
          
      });

      我制作了一个playground组合了所有四个版本。

2 个答案:

答案 0 :(得分:7)

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

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;移动 out p
  • 但我们稍后在p
  • 中使用assert_eq!(*p, 2.718);

合理的解释是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安全的别名XOR Mutability原则。 < / p>

答案 1 :(得分:3)

我无法解释这一点,但你可以做一个与隐式取消引用相似的技巧,并说r&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);
}