Disclaimer: not quite satisfied with the answers so far. I think a more in-depth answer would be more valuable.个
Ownership & Borrowing
Rust中处理值(和引用)的两个基石概念是所有权和borrow :
- 所有权可以转移:值从一个变量移到另一个变量.
- 所有权可以暂时"借出":创建一个对值的引用(可变或不可变),借入它.
也可以克隆(或复制)一个值,在这种情况下,将创建一个独立于原始值的new值.在这种情况下,不会发生所有权转移,并且在新值可用时,对克隆(或复制)的值的borrow 结束.
复制引用
不可变(共享)引用是可复制的(它们实现了Copy
特征),因为您可以在任何时刻拥有对给定值的任意数量的不可变引用.
但是,可变(唯一)引用不是唯一的,否则它们就不是唯一的.
这意味着将引用赋给变量将具有不同的属性,具体取决于它是否是不可变的:
let x = 3;
let y = &x;
let z = y; // z is a Copy of y
let mut a = 7;
let b = &mut a;
let c = b; // b is _moved_ to c, the variable b cannot be used afterwards.
后者是temporarily能够从b
borrow a
的引用(或可变引用)的关键动机,例如,进行函数调用:
fn foo(r: &mut i32) {
*r += 1;
}
fn main() {
let mut a = 7;
let b = &mut a;
foo(b);
println!("{b}"); // Expected error, `b` was moved out.
}
录入再借款
上述问题的解决方案是re-borrowing的概念,即暂时borrow reference中的referred值.
关键的 idea 是,从一个价值中借款是very similarly%的效果.当您从某个值借入时:
- 您borrow 的变量是完好无损的.
- 在可变borrow 的情况下,该变量在borrow 的生存期(引用生存期的子集)内是不可访问的.
因此,再借款可以实现同样的关键理念:
- 您borrow 的参照是完好无损的.
- 在borrow 的整个生命周期内,您borrow 的引用是不可访问的--在可变borrow 的类型中.
再借入的句法是bit的特殊之处.您不能写&reference
,因为这会创建对引用的引用,这是有效的,但不完全是您想要的.因此,您只需要编写&*reference
来表明您希望引用reference
本身所指的内容.
由于这种语法相当冗长,而且不太符合人体工程学,因此通常会为您完成automatically次重复borrow .以我们前面的例子为例:
fn foo(r: &mut i32) {
*r += 1; // Re-borrow 1
}
fn main() {
let mut a = 7;
let b = &mut a;
foo(b); // Re-borrow 2
println!("{b}");
}
从底部开始,当使用可变引用调用接受可变引用的函数时,编译器自动插入&mut *
,因此实际上示例not确实会导致编译器错误,即使b
不能被复制,只能被移动.
同样,这*r += 1
实际上是在再借款.这一次*
是手动插入的,编译器然后隐式应用&mut
,因为它将表达式重写为(*r).add_assign(1)
,其中add_assign
取&mut self
.
再贷款无处不在,只是它是如此的自动,你可能从来没有注意到.
申请再借款
让我们判断一下您的示例,看看到底是怎么回事:
fn main() {
let mut foo = 1;
let mut borrower = &mut foo;
let rr = &mut borrower; // (1)
let borrower2 = &mut (**rr); // (2)
*borrower2 = 2; // (3)
*borrower = 3; // (4)
println!("{}", foo);
}
按顺序:
- Creates a mutable reference to a mutable reference to
foo
:
rr
是&mut &mut i32
类型的.
- 现在借了
borrower
美元.
- Create a mutable reference to
foo
:
borrower2
是&mut i32
类型的.
rr
现在是reborrowed.
- 赋值为
borrower2
所指的值.
- 赋值为
borrower
所指的值.
为什么允许(4)?
简短的回答是,借入rr
到borrower2
的生命周期 结束,借入borrower
到rr
的生命周期 结束,因此borrower
不再被借入.
长长的答案是,originally Rust编译器将考虑在其引用的生存期内borrow 变量,该生存期通常直到创建该引用的作用域的末尾.这是不灵活的,需要引入额外的作用域:
fn main() {
let mut foo = 1;
let mut borrower = &mut foo;
{
let rr = &mut borrower; // (1)
let borrower2 = &mut (**rr); // (2)
*borrower2 = 2; // (3)
}
*borrower = 3; // (4)
println!("{}", foo);
}
为了让我们的工作更轻松,实现了非词法生存期(NLL),这给了编译器结束借入earlier的自由,而不是创建借入的引用范围的结束.一般来说,这意味着借入只持续到引用的第last use位.
因此,您的示例无需(手动)额外作用域即可工作.
如果你把作业(job)颠倒过来呢?即:
fn main() {
let mut foo = 1;
let mut borrower = &mut foo;
let rr = &mut borrower; // (1)
let borrower2 = &mut (**rr); // (2)
*borrower = 3; // (A)
*borrower2 = 2; // (B)
println!("{}", foo);
}
好吧,那么NLL不会再拯救你了,因为它只收缩了借款的tail,而不是head.它could理论上只从第一次使用到最后一次使用,而不是从创作到最后一次使用,但它没有,因此:
borrower2
最后一次用在(B).
- 因此,必须从(2)到(B)borrow
rr
.
- 因此,必须从(1)到(B)borrow
borrower
.
- 因此,
borrower
是在(A)处借入的--再次try 再次借入是错误的.
安全了吗?