如何重新借用可变引用而不将其传递给函数? [英] How can I reborrow a mutable reference without passing it to a function?

查看:33
本文介绍了如何重新借用可变引用而不将其传递给函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我发现了一种情况,手动内联函数会改变借用检查器处理它的方式,使其不再编译.据推测,它依赖于函数签名中的信息.我如何在内联版本中提供此信息?

我认为它是如何工作的

'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 主(){让 mut x: f32 = 3.142;让 mut p = &mut x;{让 q = &mut p;让 r = 再借(q);*r = 2.718;}assert_eq!(*p, 2.718);}

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

手动内联

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

fn main() {让 mut x: f32 = 3.142;让 mut p = &mut x;{让 q = &mut p;让 r = *q;<-- 此处报告错误.*r = 2.718;}assert_eq!(*p, 2.718);}

error[E0507]: 不能移出借来的内容

  1. 谁拿走了我的玩具?什么是类型推断思维/缺失?

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

其他一些尝试

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

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

这里有一个变通方法,它定义了名称 r 并起作用,但不使用相同的借用和取消引用序列:

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

我制作了一个 playground 结合了所有四个版本.

解决方案

显而易见的解决方案有效,正如人们所料:

fn main() {让 mut x: f32 = 3.142;让 mut p = &mut x;{让 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() {让 mut x: f32 = 3.142;让 mut p = &mut x;{让 r: &mut f32 = &mut *p;*r = 2.718;}assert_eq!(*p, 2.718);}

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

<块引用>

error[E0502]: 不能借用 `p` 作为不可变的,因为 `*p` 也被借用为可变的--><匿名>:6:24|5 |让 r: &mut f32 = p;|- 此处发生可变借用6 |println!("{}", p);|^ 不可变借用发生在这里7 |*r = 2.718;8 |}|- 可变借用到此结束错误:由于先前的错误而中止

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

1 可变引用不能是 Copy 甚至 Clone,因为它会违反支持 Rust 安全性的 Aliasing XOR 可变性原则.

I have found a case where manually inlining a function changes the way the borrow-checker treats it, such that it no longer compiles. Presumably it is relying on the information in the function signature. How can I provide this information in the inlined version?

How I think it's working

Let 'a and 'b be lifetimes with 'a shorter than 'b (which can be written 'b: 'a).

Suppose I have a p: &'b mut f32. I can borrow p briefly (with &mut p) to obtain q: &'a mut &'b mut f32.

  1. Have I correctly understood that &'a mut &'b mut f32 is equivalent to &'a mut &'a mut f32 because 'b: 'a?

I can then dereference q (with *q) to obtain r: &'a mut f32. I can write to the f32 via r (with *r = something), and I can later (outside lifetime 'a) read back the value via p (with *p).

With a function call

Here is some working code that I think uses the above sequence:

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);
}

(Replacing *q with q in the body of reborrow() also works, because Rust inserts the necessary dereference if it is missing).

Manually inlined

If I manually inline the reborrow() call, it no longer compiles:

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. Who took away my toys? What is the type inference thinking/missing?

  2. Can I annotate the let binding somehow to make the compiler infer the same types as in the previous version?

Some other attempts

Here's another version that works, but which doesn't define the name 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);
}

Here's a work-around that defines the name r and works, but does not use the same sequence of borrows and dereferences:

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);
}

I made a playground combining all four versions.

解决方案

The obvious solution works, as one could expect:

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);
}

It seems relatively intuitive and is what I would expect a newcomer to end up with.


If you start thinking about it, however, it will not make sense. As described, it looks like:

  • let r: &mut f32 = p; moves out of p
  • and yet we use p later in assert_eq!(*p, 2.718);

A reasonable explanation would be that p is Copy, however it's not1!

The answer is that, implicitly, Rust is performing a re-borrowing behind the scenes. That is, the explicit code is:

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);
}

We can check this by attempting to read p after re-borrowing it, and check the compiler error:

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

Which confirms that p is indeed only borrowed mutably, and not moved, cloned or copied.

1 A mutable reference cannot be Copy or even Clone as it would violate the Aliasing XOR Mutability principle which underpins Rust safety.

这篇关于如何重新借用可变引用而不将其传递给函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆