rust book人起:

在任何给定时间,您可以有一个可变引用,也可以有任意数量的不可变引用.

请考虑以下代码:

fn main() {
    let mut a = 41;
    let b = &mut a;
    let c: &i32 = &a; // [1]
    let d: &i32 = &b;

    println!("{} {}", b, c); // [2]
    println!("{} {}", b, d);
}

如果我们try 编译,我们会得到:

error[E0502]: cannot borrow `a` as immutable because it is also borrowed as mutable
  --> src\main.rs:43:19
   |
42 |     let b = &mut a;
   |             ------ mutable borrow occurs here
43 |     let c: &i32 = &a; // [1]
   |                   ^^ immutable borrow occurs here
44 |     let d: &i32 = &b;
   |                   -- mutable borrow later used here

...因此,关于只有一个可变引用的规则是成立的.

但是,如果您对标记为[1][2]的行进行注释,则一切都可以正常编译.请注意,在这种情况下,b是可变引用,而d是不可变引用(似乎与c相同).

为什么我们允许这种情况,为什么这个用例在编译时没有违反关于拥有一个可变引用或n个不可变引用的规则?

推荐答案

在这里,这本书简化了事情,以便更快地传达信息.实际上,您可以想要多少个可变引用就拥有多少个.

let mut a = 41;
let b = &mut a;
let c = &mut *b;
f(c);
let d = &mut *c;
let e = c;

实际的限制是,任何时候只能有一个可变引用为active.这也适用于最初的所有者.

因此,当上面创建c时,您可以进入一个区域,在该区域中,您可以将c传递给f等函数,使用c创建d等其他borrow ,或更改c的所有权,如e.在最后一次使用c之前,你不能用b来做任何一件事.当你创建d的时候,你进入了另一个不能使用c的地区.由于d从未使用过,因此该区域立即结束,而c再次变为活动状态.

这应该是有意义的,因为如果没有它,可变引用将是极其有限的.例如,每个接受像fn f(x: &mut i32)这样的可变引用的函数都会从您传递给它的内容中创建一个新的临时borrow .如果不是这样,您将只能使用一次可变的借入.

这解释了为什么您的原始代码不能工作.当b处于活动状态时,您正在try 使用a.

以下是你的第二个版本:

fn main() {
    let mut a = 41;
    let b = &mut a;
    // let c: &i32 = &a; // [1]
    let d: &i32 = &b;

    // println!("{} {}", b, c); // [2]
    println!("{} {}", b, d);
}

我将进行两项更改,但不会更改任何借款关系.首先,编译器自动将let d: &i32 = &b;扩展为let d: &i32 = &*b;.如果没有,这将try 将&&mut i32赋给类型为&i32的变量,并且不会进行编译.其次,为了方便起见,println!宏添加了对其每个参数的引用.这是只有宏才能以不可见的方式完成的事情;当函数接受引用时,它总是显式的.我用一个函数替换了println!,这样您就可以看到每个变量实际上是如何被borrow 的.

fn main() {
    let mut a = 41;
    let b = &mut a;
    let d: &i32 = &*b;

    print(&b, &d);
}

fn print(x: &&mut i32, y: &&i32) {
    println!("{} {}", *x, *y);
}

从我上面写的来看,这似乎行不通,因为你先用b,然后再用d.但允许可变引用(以及所有者和不可变引用)做的另一件事是:生成任意数量的不可变引用.一旦一个不可变的引用被给出,你就进入一个区域,在那里你可以使用不可变的引用and创建更多的不可变的引用.在本例中,b创建了一个对其内容的不可变引用(本质上是&a,类型为&i32)和一个对自身的不可变引用(类型为&&mut i32).

考虑到这一点,上面的代码如下所示:

  • a被创建为所有者
  • a创建可变引用b并完全处于非活动状态
  • b创建不可变的引用d并部分变为非活动状态
  • 另一个不变的引用被创建为b本身(仍部分处于非活动状态)
  • 创建对d本身的不可变引用
  • 这两个都传递到print

就在main结束之前,给予print的两个引用和引用d到期,使b再次激活.然后b过期,使a再次处于活动状态.然后a被丢弃,main结束.

Rust相关问答推荐

有条件默认实现

为什么拥有的trait对象的相等运算符移动了正确的操作数?

为什么我的梅森素数代码的指数越大,速度就越快?

访问Rust中的隐藏变量

抽象RUST中的可变/不可变引用

使用 struct 外部的属性来改变 struct 的原始方式

为什么std repeat trait绑定在impl块和关联函数之间?

如何使用盒装枚举进行模式匹配?

如何用Axum/Tower压缩Html内容?

在0..1之间将U64转换为F64

类型生命周期绑定的目的是什么?

tokio::sync::broadcast::Receiver 不是克隆

.to_owned()、.clone() 和取消引用 (*) 之间有区别吗?

go 重并堆积MPSC通道消息

有没有办法隐式绑定 let/match 操作的成员?

打印 `format_args!` 时borrow 时临时值丢失

将 Futures 的生命周期特征绑定到 fn 参数

在空表达式语句中移动的值

覆盖类型的要求到底是什么?为什么单个元素元组满足它?

A 有一个函数,它在 Option<> 类型中时无法编译,但在 Option<> 类型之外会自行编译.为什么?