如何理解闭包参数

How to understand closure parameters

我是 Rust 的初学者。我试着写了一个链表来练习。

一些结构代码:

type Link<T> = Option<Box<Node<T>>>;

pub struct List<T> {
    head: Link<T>,
}

struct Node<T> {
    elem: T,
    next: Link<T>,
}

列表的查看功能:

错误代码:

pub fn peek(&self) -> Option<&T> {
    self.head.map(|ref node| {
        &node.elem
    })
}

Rust 编译器 return:cannot return value referencing function parameter

下面的代码就可以了:

pub fn peek(&self) -> Option<&T> {
    self.head.as_ref().map(|node| {
        &node.elem
    })
}

所以我猜,

错误代码实际上是以下代码的缩写:

self.head.map(|node| {
      let ref ref_node = node;
      &ref_node.elem
})

我的猜测是否正确?

更多,Rust中有没有类似的缩写?或者缩写规则。

错误“无法 return 值引用函数参数”在这里有点转移注意力;它与根本问题无关,只是恰好列在第一位。如果编译代码

type Link<T> = Option<Box<Node<T>>>;

pub struct List<T> {
    head: Link<T>,
}

struct Node<T> {
    elem: T,
    next: Link<T>,
}

impl<T> List<T> {
    pub fn peek(&self) -> Option<&T> {
        self.head.map(|ref node| &node.elem)
    }
}

然后你会得到两个错误,第二个错误是这段代码不起作用的原因,你需要 .as_ref():

error[E0507]: cannot move out of `self.head` which is behind a shared reference
  --> src/lib.rs:14:9
   |
14 |         self.head.map(|ref node| &node.elem)
   |         ^^^^^^^^^ move occurs because `self.head` has type `Option<Box<Node<T>>>`, which does not implement the `Copy` trait
   |
help: consider borrowing the `Option`'s content
   |
14 |         self.head.as_ref().map(|ref node| &node.elem)
   |                  +++++++++

这个错误是根本原因; Option::map 消耗它的输入 Option,这不是我们想要在这里做的。 Option::as_ref 通过采用 &Option<T>(此处,通过隐式 auto-reference 将 self.head 转换为 &self.head)和 return 新创建的 Option<&T>,然后可以由 Option::map.

消耗

但是,我知道您想知道错误代码是如何产生“cannot return...”错误的。方法如下:

  1. 我们知道 self.head 是一个 Option<Box<Node<T>>>
  2. 对该类型调用 Option::map() 意味着它需要一个函数 FnOnce(Box<Node<T>>) -> &T.
  3. 也就是说,闭包充当一个函数,它被赋予 Box<Node<T>> 所有权。因此,在闭包中,node 是一个拥有 Box.
  4. 的局部变量
  5. 闭包的主体然后尝试 return 对该框内 Node 的字段的引用,这是一个错误 E0515,因为该框由局部变量所有并且是将在函数结束时被删除。

你的 ref 修饰符没有改变,因为一个值是否被移入函数是由函数的参数类型决定的,而不是由函数体使用的 pattern/binding 类型决定的。

The error code is actually an abbreviation of the following code:

self.head.map(|node| {
      let ref ref_node = node;
      &ref_node.elem
})

Is my guess correct?

是也不是。您可以那样扩展代码。但这不是“真正的”代码;对于函数参数,更复杂的模式总是有效之前引入单独的普通变量 node 。无论如何,这不是编译器所做的事情;它添加了简单的代码,就像乘以 1 不会改变数字一样。但是,您 可以 在不更改程序的情况下执行此操作的事实是,无论函数本身在其参数模式中写入什么,函数参数都会被移入。

但是,不可能通过将 Rust 函数参数更改为以这种方式工作来使您的原始代码工作。导致问题的函数是 Option::map() 应用于借用的结构字段;无论传递给 map() 的函数做什么,它总是会失败,因为 Option::map() 总是消耗给它的 Option