一直对 Rust 中的闭包似懂非懂, 用的时候基本都是以编译器不报错为准, 报错了就按编译器提示的改。今天有点时间,把 the book 中的关于闭包的部分从头到尾看了一遍,发现自己原来的理解不说是入木三分吧,至少也是南辕北辙。
1.闭包跟函数(方法)的区别
貌似在这点上,大部分的语言的标准都还是比较一致的, 两者的最大的区别就是闭包可以捕获上下文中的变量, 简单来说就是在闭包里可操作的变量不单单是你显式传给闭包的变量,理论上在闭包所存在的作用域中, 所有在闭包之前定义的变量都可以为闭包所用。而这也应该是 closure 这个名字的由来。
2.Rust 中的闭包
Constraint or Trait?
因为 Rust 独有的 ownership 内存管理方式,导致闭包这个重点针对上下文中变量所创造出来的概念变的复杂了。你把上下文中的变量都拿到你的 body 里去了,你操作完之后拍拍屁股走了,让外层的函数们怎么办?要是只让借不让拿,这倒是个办法,但是有些情况下,我就是要将变量挪到闭包里去(比如说某些用到多线程的时候),这时候如果不让转移所有权,代码写起来也会很麻烦。Rust 面对这种情况还是选择了它惯用的办法,它定标准,你按它的标准声明你的代码,它再检查你的代码是不是符合你的声明。Rust 一共提供了三种标准, FnOnce
, FnMut
, Fn
。它们实际上是三个标准库中的三个 trait, 但之所以说它们是标准而不是 trait, 是因为我们定义的闭包的函数签名不会因为我们要实现其中的任意一种而变得不一样, 而且我们也不需要去手动实现这三个 trait 的方法, 编译器会根据你写的闭包的实际代码来判断你的这个闭包符合以上三个当中的哪个标准。而且这三个标准对于闭包对变量的使用限制是逐级增强的,在要求较低标准闭包的地方传入符合较高标准的闭包是允许的, 比如有个函数要求传入 FnOnce 级别的闭包, 你实际传入的是个可以符合 FnMut 标准的闭包,这是完全可以的, 但是反过来不行, 也就是人家要求高标准的闭包, 你传给人家个低标准的,编译器就会报错
标准是什么?
既然这哥仨是标准, 那它们的定义或者说要求是什么呢?编译器又是如何检测你提供的闭包是符合哪个标准的呢?
以下是摘自 the book 的原文:
Closures will automatically implement one, two, or all three of these Fn traits, in an additive fashion:
FnOnce applies to closures that can be called at least once. All closures impl