Background

考虑一个玩具问题,我有一个Node struct ,它表示链表的 node ,并且我想创建一个函数,该函数用1到9的值构建一个列表.以下代码按预期工作:

struct Node {
    val: i32,
    next: Option<Box<Node>>,
}

fn build_list() -> Option<Box<Node>> {
    let mut head = None;
    let mut tail = &mut head;
    for n in 1..10 {
        *tail = Some(Box::new(Node {val: n, next: None}));
        if let Some(ref mut x) = tail {
            tail = &mut x.next;
        };
    }
    head
}

但如果我将build_list函数中的匹配表达式修改为以下内容,它将无法编译:

fn build_list() -> Option<Box<Node>> {
    let mut head = None;
    let mut tail = &mut head;
    for n in 1..10 {
        *tail = Some(Box::new(Node {val: n, next: None}));
        if let Some(x) = tail.as_mut() {
            tail = &mut x.next;
        };
    }
    head
}

编译错误:

error[E0506]: cannot assign to `*tail` because it is borrowed
  --> src/main.rs:72:9
   |
72 |         *tail = Some(Box::new(Node {val: n, next: None}));
   |         ^^^^^
   |         |
   |         assignment to borrowed `*tail` occurs here
   |         borrow later used here
73 |         {
74 |             if let Some(x) = tail.as_mut() {
   |                              ---- borrow of `*tail` occurs here

error[E0499]: cannot borrow `*tail` as mutable more than once at a time
  --> src/main.rs:74:30
   |
74 |             if let Some(x) = tail.as_mut() {
   |                              ^^^^ mutable borrow starts here in previous iteration of loop

Question

在这个例子中,两者的区别是什么

if let Some(ref mut x) = tail

if let Some(x) = tail.as_mut()

?

(作为一名学习Rust 的初学者)我本以为这些匹配表达式是等效的,但显然我忽略了一些细微的差别.

使现代化

I cleaned up the code from my original example so that I don't need a placeholder element for the head of the list. The difference (和 compilation error) still remains, I just get an additional compilation error for assigning to borrowed *tail.

使现代化 2

(这只是一个奇怪的观察,无助于回答最初的问题)

fn build_list() -> Option<Box<Node>> {
    let mut head = None;
    let mut tail = &mut head;
    for n in 1..10 {
        *tail = Some(Box::new(Node {val: n, next: None}));
        if let Some(ref mut x) = tail {
            if n % 2 == 0 {
                tail = &mut x.next;
            }
        };
    }
    head
}

新错误:

error[E0506]: cannot assign to `*tail` because it is borrowed
  --> src/main.rs:60:9
   |
60 |         *tail = Some(Box::new(Node {val: n, next: None}));
   |         ^^^^^
   |         |
   |         assignment to borrowed `*tail` occurs here
   |         borrow later used here
61 |         if let Some(ref mut x) = tail {
   |                     --------- borrow of `*tail` occurs here

error[E0503]: cannot use `*tail` because it was mutably borrowed
  --> src/main.rs:61:16
   |
61 |         if let Some(ref mut x) = tail {
   |                ^^^^^---------^
   |                |    |
   |                |    borrow of `tail.0` occurs here
   |                use of borrowed `tail.0`
   |                borrow later used here

error[E0499]: cannot borrow `tail.0` as mutable more than once at a time
  --> src/main.rs:61:21
   |
61 |         if let Some(ref mut x) = tail {
   |                     ^^^^^^^^^ mutable borrow starts here in previous iteration of loop

推荐答案

代码的第二个版本失败的原因是rust的方法/函数总是borrow 整个对象,而不是它们的一部分.

在你的例子中,这意味着tail.as_mut()可变地borrow tail,并且只要tail被使用,这种borrow 就会一直有效:

...
    for n in 1..10 {
        *tail = Some(Box::new(Node {val: n, next: None})); // Error in the second iteration,
                                                           // 'tail' was already borrowed
        if let Some(x) = tail.as_mut() { // <-+ Borrow of 'tail' starts in the first iteration
            tail = &mut x.next;          // <-+ 'tail' now borrows itself
        };                               //   |
    }                                    // <-+ Borrow of 'tail' ends here, after the last iteration
...

因为xtail的借位,&mut x.next也是tail的借位,这意味着tail = &mut x.next本身就是tail的借位.因此,只要tail在范围内,tail的初始借款就不能超出范围.tail在每次迭代中都会使用,因此borrow 只能在循环的最后一次迭代后超出范围.

现在,为什么build_list的第一个版本可以工作?简而言之:因为tail从来都不是借来的.if let Some(ref mut x) = tail是将tail分解为其组件(在本例中为Option::Somex).这并不是borrow tail作为一个整体,它只是borrow x.当你使用tail = &mut x.next时,你现在也可以将x分解成它的组件(只提取next),然后使用tailborrow 它.在下一次迭代中,tail不是借来的,因此可以愉快地重新分配.

方法/函数调用受到限制,因为它们不知道稍后将使用对象的哪些部分.因此,as_mut()必须borrow 整个tail,即使你只使用其中的一部分.这是类型系统的一个限制,也是为什么getter/setter方法比直接调用 struct 的成员更弱/更具限制性的一个原因:getter/setter将迫使您borrow 整个 struct ,而直接访问成员只会borrow 该成员,而不会borrow 其他成员.

Rust相关问答推荐

为什么这些From A和From B植入会导致重复的实现错误?

如何初始化match声明中的两个变量而不会激怒borrow 判断器?

计算具有相邻调换且没有插入或删除的序列的距离

把Vector3变成Vector4的绝妙方法

关于如何初始化弱 struct 字段的语法问题

在不重写/专门化整个函数的情况下添加单个匹配手臂到特征的方法?

如何获取Serde struct 的默认实例

如何实现泛型枚举的`Serde::Desialize`特性

在UdpSocket上使用sendto时的隐式套接字绑定

铁 rust 中的共享对象实现特征

如何在Rust中将选项<;选项<;字符串>;转换为选项<;选项&;str>;?

为什么RefCell没有与常规引用相同的作用域?

装箱特性如何影响传递给它的参数的生命周期 ?(举一个非常具体的例子)

用于实现获取 struct 体 id 的特征规范

我如何取消转义,在 Rust 中多次转义的字符串?

哪些特征通过 `Deref` 而哪些不通过?

为什么我不能克隆可克隆构造函数的Vec?

如何连接 Rust 中的相邻切片

`map` 调用在这里有什么用吗?

你能用 Rust 和 winapi 制作 Windows 桌面应用程序吗?