为什么在闭包参数中使用"&&"?

31

关于这个示例,我有两个问题:

let a = [1, 2, 3];

assert_eq!(a.iter().find(|&&x| x == 2), Some(&2));
assert_eq!(a.iter().find(|&&x| x == 5), None);
  1. 为什么在闭包参数中使用 &&x 而不是只用 x?我知道 & 是传递对象的引用,但使用两次它意味着什么?

    我不理解文档所说的:

    因为 find() 接受一个引用,而许多迭代器迭代引用,这会导致一个可能令人困惑的情况,即参数是一个双重引用。你可以在下面的示例中看到这种效果,其中使用了 &&x

  2. 为什么要使用 Some(&2) 而不是 Some(2)

2个回答

37

a 是类型为 [i32; 3] 的数组,包含三个 i32

[i32; 3] 没有实现 iter 方法,但它可以解引用成 &[i32]

&[i32] 实现了一个可以生成迭代器的 iter 方法这个迭代器 实现了 Iterator<Item=&i32> 接口。

使用 &i32 而不是 i32 是因为迭代器需要处理任何类型的数组,而不是所有类型都能够安全地复制。所以,它通过引用而不是值来迭代元素,而不是限制于可复制的类型。

find 是适用于所有 Iterator 的方法。它让你查看每个元素并返回与谓词相匹配的元素。问题在于:如果迭代器产生的值是不可复制的,则将该值传递给谓词会使其无法从 find 中返回。由于迭代器通常不可倒带或重新启动,因此该值无法重新生成。因此,find 必须通过引用而不是值将元素传递给谓词。

因此,如果你有一个实现了 Iterator<Item=T> 的迭代器,那么 Iterator::find 需要一个接受 &T 并返回 bool 的谓词。[i32]::iter 生成了一个实现 Iterator<Item=&i32> 的迭代器。因此,在数组迭代器上调用 Iterator::find 需要一个接受 &&i32 的谓词,也就是它向谓词传递指向该元素的指针的指针。

因此,如果您编写a.iter().find(|x| ..),则x的类型为&&i32。这不能直接与字面值i322进行比较。有几种修复方法。一种是显式解引用xa.iter().find(|x| **x == 2)。另一种是使用模式匹配来解构双重引用:a.iter().find(|&&x| x == 2)。在这种情况下,这两种方法做完全相同的事情。[1]
至于为什么使用Some(&2):因为a.iter()是一个迭代器,遍历的是&i32,而不是i32。如果您查看Iterator :: find的文档,您将看到对于Iterator<Item=T>,它返回一个Option<T>。因此,在这种情况下,它返回一个Option<&i32>,因此必须将其与之进行比较。

[1]:区别只在非Copy类型时才有意义。例如,|&&x| ..无法在&&String上工作,因为您必须能够从引用后面移动String,而这是不允许的。然而,|x| **x ..将起作用,因为它只是在不移动任何内容的情况下深入引用内部。


4
谢谢!另外,如果有人不理解什么是“解引用成&[i32]”,根据这里,一个数组a可以自动借用为一个切片&a - Sajuuk

4

1) 我认为书中的解释很好,下面我用.cloned()的例子可能会有用。但是由于.iter()迭代引用,所以你必须另外指定引用,因为find需要一个引用。

2) .iter()正在迭代引用; 因此,您会找到一个引用。

您可以使用.cloned()来查看如果不必处理引用,它会是什么样子:

assert_eq!(a.iter().cloned().find(|&x| x == 2), Some(2));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接