重新借用可变引用

kwo*_*yul 18 reference lifetime rust mutable-reference

当我想知道可变引用如何转移到方法中时,所有问题都开始了。

let a = &mut x;
a.somemethod(); // value of a should have moved
a.anothermethod(); // but it works.
Run Code Online (Sandbox Code Playgroud)

我用谷歌搜索了很多。(真的很多)而且我注意到作为参数传递给函数的可变引用总是会经历以下转换。(这称为再借)

fn test(&mut a) -> ();

let a = &mut x;
test(a); // what we write in code.
test(&mut *a); // the actual action in code.
Run Code Online (Sandbox Code Playgroud)

因此,我在谷歌上搜索了更多有关“再借”的详细信息。

这就是我所拥有的。

在任何代码中,x指的是任意数据。我没有提及它,因为我认为它的类型对于讨论来说并不重要。(不过,我自己用的是i32)。

let a = &mut x;
let b = &mut *a; // a isn't available from now on
*a = blahblah; // error! no more access allowed for a
*b = blahblah; // I've wrote this code not to confuse you of the lifetime of b. If not mentioned, it always live till the scope ends.
Run Code Online (Sandbox Code Playgroud)
let a = &mut x;
{
    let b = &*a;
    // *a = blahblah in this scope will throw an error, just like above case.
}
*a = blahblah; // but this works.
Run Code Online (Sandbox Code Playgroud)

那么好吧。这很有趣。看来b不仅是借x,还借a

也许,我们可以这样澄清重借:&'a *(&'b mut x)。它借了x(这里有一个生命周期的“a”),但也借了a(这里有一个生命周期的“b”)。

所以我运行了以下代码来证实我的猜想。

let x: i32 = 1; // I wanted to make it clear that x lives in this scope.
let b;
{
    let a = &mut x;
    b = &mut *a;
}
*b = 0;
Run Code Online (Sandbox Code Playgroud)

但这有效!

什么??但我刚刚决定得到这个。 &'a mut *&mutx, 不是&'a mut *&'b mutx

我不知道为什么mut x在 的生命周期内不可用,&mut *&mutx也不知道为什么mut x在 的生命周期后重新可用&mut *&mutx,但是“好吧,让我们这么说吧”。

但看看这个。我完全无法获得清晰而普遍的理解。

let x: i32 = 1;
let b;
{
    let a = &mut x;
    let b = &**&a;
} // error!! a should live longer than b!
Run Code Online (Sandbox Code Playgroud)

寿命不就是仅仅依赖于真实数据b所指的吗??? &'a **& &mut x, 不是&'a **&'b &'c mut x

现在怎么办?

&'a **&'b &mut x??? (这是我的猜测)。

我该如何接受这个复杂的局面呢?

Cod*_*256 12

这些都是一些很好的问题!我会尽力回答我能回答的问题。

Rust 参考是寻找此类问题的答案的好地方,有关 Rust 的更深层次语义。

首先,对于您关于方法解析的问题,参考文献说

当查找方法调用时,接收者可能会自动取消引用或借用以调用方法。这需要比其他函数更复杂的查找过程,因为可能有许多可能的方法可供调用。使用以下过程:

第一步是建立候选接收者类型列表。通过重复取消引用接收者表达式的类型,将遇到的每种类型添加到列表中,最后在最后尝试未调整大小的强制转换,如果成功则添加结果类型来获取这些类型。然后,对于每个候选者T,将 &T&mut T立即添加到列表中的 后面T

例如,如果接收者的类型为Box<[i32;2]>,则候选类型将为Box<[i32;2]>&Box<[i32;2]>&mut Box<[i32;2]>[i32; 2](通过解引用)、&[i32; 2]&mut [i32; 2][i32](通过未调整大小的强制)、&[i32]、 最后&mut [i32]

上面的链接有更详细的介绍。

对于您的其余问题,我认为这主要与类型强制有关。一些相关的强制是

  • &mut T&T
  • &T&mut T如果实现&UTDeref<Target = U>
  • &mut T如果实现&mut UTDerefMut<Target = U>

值得注意的是,&U&mut U实现了Deref<Target = U>&mut U也实现了DerefMut<Target = U>。因此,第二/第三条规则导致以下强制:

T 强制来自
&U &&U &U
&U &mut &U &U
&mut U &&mut U &U
&mut U &mut &mut U &mut U

现在,参考资料已经完成,让我们更详细地看看您的问题:

let a = &mut x;
let b = &mut *a; // a isn't available from now on
*a = blahblah; // error! no more access allowed for a
*b = blahblah; // I've wrote this code not to confuse you of the lifetime of b. If not mentioned, it always live till the scope ends.
Run Code Online (Sandbox Code Playgroud)

为了写入引用,它显然必须是可变(又名唯一)引用。当你写作时let a = &mut x,现在a是访问的唯一途径x。现在当你写的时候let b = &mut *a,它本质上意味着let b = &mut *(&mut x),简化为let b = &mut x

在这里,b可变地借用a,Rust 不会让你使用,a直到b被销毁......或者目前看来是这样。

let a = &mut x;
{
    let b = &*a;
    // *a = blahblah in this scope will throw an error, just like above case.
}
*a = blahblah; // but this works.
Run Code Online (Sandbox Code Playgroud)

这里,let b = &*a变成let b = &*(&mut x),所以let b = &x。无论哪种引用b,都是可变引用并且必须是唯一的,因此在消失(超出范围)a之前您不能使用它。b

let mut x: i32 = 1; // I wanted to make it clear that x lives in this scope.
let b;
{
    let a = &mut x;
    b = &mut *a;
}
*b = 0;
Run Code Online (Sandbox Code Playgroud)

(我假设你的意思是let mut x)。

现在这就是有趣的地方。在这里,由于ab都可变地指向同一个对象,Rust 不会让你使用,a直到b被销毁。似乎推理应该是“因为b可变借用a”,但实际上并非如此。通常情况下,b实际上会借用,对于诸如、等a智能指针来说就是这种情况。但是,引用得到特殊处理:Rust 可以分析它们指向的内存。Boxstd::cell::RefMut

所以当你写的时候let b = &mut *ab实际上并不是借a!它仅借用数据a点。这种区别以前并不相关,但在这里它允许代码编译。

至于你的最后一个例子,我无法让它按照你描述的方式打破。