如何在不将其传递给函数的情况下重新导入可变引用?

apt*_*002 7 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(with *q)获取r: &'a mut f32.我可以写入f32via r(with *r = something),我可以稍后(在生命之外'a)通过p(with *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);
}
Run Code Online (Sandbox Code Playgroud)

(更换*qq在的身体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);
}
Run Code Online (Sandbox Code Playgroud)
error[E0507]: cannot move out of borrowed content
Run Code Online (Sandbox Code Playgroud)
  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);
}
Run Code Online (Sandbox Code Playgroud)

这是一个定义名称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);
}
Run Code Online (Sandbox Code Playgroud)

我做了一个结合所有四个版本的游乐场.

Mat*_* M. 9

人们可以预期,显而易见的解决方案有效:

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);
}
Run Code Online (Sandbox Code Playgroud)

它似乎相对直观,是我期望新人最终得到的.


但是,如果你开始考虑它,它将没有意义.如上所述,它看起来像:

  • let r: &mut f32 = p;移动出来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);
}
Run Code Online (Sandbox Code Playgroud)

我们可以通过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
Run Code Online (Sandbox Code Playgroud)

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

1 可变引用不能Copy或甚Clone至不会违反支持Rust安全性的Aliasing XOR Mutability原则.


She*_*ter 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);
}
Run Code Online (Sandbox Code Playgroud)