fn sort_by_statistic(cities: &mut Vec<City>, stat: Statistic) {
    cities.sort_by_key(|city| -city.get_statistic(stat));
}

Rust 在创建闭包时会自动借用对 stat 的引用。这合情合 理:闭包引用了 stat,因此必须有一个对它的引用

Rust 使用生命期而不是垃圾回收确保代码安全,但 Rust 的方式更快。即使是快速的 GC 分配也会比 Rust 把 stat 保存在栈 上慢。

use std::thread;
fn start_sorting_thread(mut cities: Vec<City>, stat: Statistic)
    -> thread::JoinHandle<Vec<City>>
{
    let key_fn = |city: &City| -> i64 { -
city.get_statistic(stat) };
    thread::spawn(|| {
        cities.sort_by_key(key_fn);
cities })
}

thread::spawn 接收一个闭 包,并在新的系统线程中调用这个闭包

实际上这里有两个问题,因为 cities 也属于不安全的共享 thread::spawn 创建的新线程不能保证自己在 cities 和 stat 被销毁(函数结束)前完成任务。

于是需要告诉 Rust 把 cities 和 stat 到 使用它们的闭包中,而不要再引用它们。

fn start_sorting_thread(mut cities: Vec<City>, stat: Statistic)
    -> thread::JoinHandle<Vec<City>>
{
    let key_fn = move |city: &City| -> i64 { -
city.get_statistic(stat) };
    thread::spawn(move || {
        cities.sort_by_key(key_fn);
cities })
}

move 关键字告诉 Rust,这个闭包不是在借用它使用的变量,而是要 把它偷走

Rust 为闭包提供了两种从包含函数取得数据的方式:转移和借用;

在接受 Rust 严格的规则之后,我们也换来了重要的收益:线程安 全。这完全归功于向量被转移了,而不是在多个线程间共享。如果是 共享的,那么在新线程修改它时,老线程也不会放手。

闭包类型

fn city_population_descending(city: &City) -> i64 {
    -city.population
}

这个函数接收一个参数(&City),返回一个 i64 值。因此它的类型 就是 fn(&City) -> i64

函数值很小,一个 fn 值就是这 个函数机器码的内存地址,与 C++ 中的函数指针类似

一个函数可以接收另一个函数作为参数

fn count_selected_cities(cities: &Vec<City>,test_fn: fn(&City) -> bool) -> usize
{
    let mut count = 0;
    for city in cities {
test_fn: fn(&City) -> bool) -> usize
        if test_fn(city) {
count += 1; }
}
count }

或者

fn count_selected_cities<F>(cities: &Vec<City>, test_fn: F) ->
usize
    where F: Fn(&City) -> bool
{
    let mut count = 0;
    for city in cities {
        if test_fn(city) {
count += 1; }
}
count }

因为闭包可以调用,但它不是 fn。闭 包|city| city.monster_attack_risk > limit 有自己的类型,但并不 是 fn。

每个闭包都有自己的类型,因为闭包可能包含(从包 含作用域中借来或偷来的)数据.

因此编译器会为每个闭包创建一个临时类型,大 到足以存储它的数据。任何两个闭包的类型都不相同。但所有闭包都 会实现 Fn 特型,例子中的闭包就实现了 Fn(&City) -> bool

闭包的性能

闭包不会被分配到堆上,除非你把它们装到 Box、Vec 或其他容器里。而且因为每个闭包都有一个不同的类型,所 以 Rust 编译器只要知道了你所调用闭包的类型,就可以将该闭包的 代码行内化

FnOnce

fn call_twice<F>(closure: F) where F: Fn() {
    closure();
closure(); 
}

函数传入任何实现 Fn() 特型的闭包。换句话说,就是不 接收参数且返回 () 的闭包。(与函数一样,如果返回值类型是 () 则可以省略。Fn() 是对 Fn() -> () 的简写

Rust 认为非 mut 值可以安全地在线程间共享。但是,如果共享的非 mut 闭包里包含 mut 数据同样是不安全的。在多个线程里调用这个闭 包会导致各种资源争用问题,与多个线程同时读写相同的数据一样

trait Fn() -> R {
    fn call(&self) -> R;
}
trait FnMut() -> R {
    fn call_mut(&mut self) -> R;
}
trait FnOnce() -> R {
	fn call_once(self) -> R;
}

Fn 是没有调用次数限制的闭包和函数,是所有 fn 函数中最高的 一种。
FnMut 是如果闭包本身声明为 mut 也可以多次调用的闭包。 FnOnce 是如果调用者拥有闭包则只能调用一次的闭包。