函式中的閉包

閉包超棒的。那麼我們要如何把它們放到我們擁有的函式中呢?

你可以寫你自己的函式來接受閉包,但是在它裡面就沒那麼自由了,你必須決定型別。在函式外的閉包可以在 FnFnMutFnOnce 之間自行決定,但在函式內部你必須選擇其中一種。最好的理解方式是多看幾個函式簽名。這裡是其中的 .all()。我們記得它會檢查疊代器,看看所有的東西是否是 true(取決於你怎麼決定是 true 還是 false)。它的部分簽名是這樣說的:

#![allow(unused)]
fn main() {
    fn all<F>(&mut self, f: F) -> bool    // 🚧
    where
        F: FnMut(Self::Item) -> bool,
}

fn all<F>:這告訴你有個泛型 F。閉包永遠是泛型的,因為每次都是不同的型別。

(&mut self, f: F)&mut self 告訴你這是方法。你通常看到 f: F 就是閉包:這是變數名和型別。當然,fF 並沒有什麼特別之處,它們可以是不同的名字。如果想要你也可以寫成 my_closure: Closure──這並不要緊。但在簽名中,你幾乎總是會看到 f: F

接下來是關於閉包的部分:F: FnMut(Self::Item) -> bool。在這裡它決定閉包型別是 FnMut,所以它可以改變值。它改變了它所接受的疊代器 Self::Item 的值。而且它必須回傳 truefalse

這裡是個更簡單帶有閉包的簽名:

#![allow(unused)]
fn main() {
fn do_something<F>(f: F)    // 🚧
where
    F: FnOnce(),
{
    f();
}
}

這只是說它接受閉包,取得值(FnOnce = 取值),且不回傳任何東西。所以現在我們可以呼叫這個什麼都不拿的閉包,做我們想要做的事情。現在我們將會建立 Vec,然後對它進行疊代,只是展示我們可以做些什麼。

fn do_something<F>(f: F)
where
    F: FnOnce(),
{
    f();
}

fn main() {
    let some_vec = vec![9, 8, 10];
    do_something(|| {
        some_vec
            .into_iter()
            .for_each(|x| println!("The number is: {}", x));
    })
}

看個更真實的例子,我們將再次建立 City 結構體。這次 City 結構體有更多關於年份和人口的資料。它有個 Vec<u32> 來表示所有的年份,還有另一個 Vec<u32> 來表示所有的人口。

City 有兩個函式:new() 用於建立新的 City, .city_data() 有個閉包引數。當我們使用 .city_data() 時,它給我們提供了年份和人口以及閉包,所以我們可以對資料做我們想做的事情。閉包型別是 FnMut,所以我們可以改變資料。它看起來像這樣:

#[derive(Debug)]
struct City {
    name: String,
    years: Vec<u32>,
    populations: Vec<u32>,
}

impl City {
    fn new(name: &str, years: Vec<u32>, populations: Vec<u32>) -> Self {

        Self {
            name: name.to_string(),
            years,
            populations,
        }
    }

    fn city_data<F>(&mut self, mut f: F) // 我們帶入 self, 但只有 f 是泛型的 F. f 是閉包

    where
        F: FnMut(&mut Vec<u32>, &mut Vec<u32>), // 閉包接受 u32 的可變向量
                                                // 那些是年份和人口資料
    {
        f(&mut self.years, &mut self.populations) // 最後這是實際的函式. 它說
                                                  // "把 self.years 和 self.populations 用在閉包上"
                                                  // 我們可以用閉包做我們想要做的事
    }
}

fn main() {
    let years = vec![
        1372, 1834, 1851, 1881, 1897, 1925, 1959, 1989, 2000, 2005, 2010, 2020,
    ];
    let populations = vec![
        3_250, 15_300, 24_000, 45_900, 58_800, 119_800, 283_071, 478_974, 400_378, 401_694,
        406_703, 437_619,
    ];
    // 現在我們可以建立我們的城市
    let mut tallinn = City::new("Tallinn", years, populations);

    // 現在我們有 .city_data() 方法能傳入閉包. 我們可以做我們想做的任何事.

    // 首先讓我們一起放入 5 年的資料並印出來.
    tallinn.city_data(|city_years, city_populations| { // 我們可以任意稱呼輸入名稱
        let new_vec = city_years
            .into_iter()
            .zip(city_populations.into_iter()) // 兩個 Zip 在一起
            .take(5)                           // 但只有拿前 5 個
            .collect::<Vec<(_, _)>>(); // 叫 Rust 決定元組內部的型別
        println!("{:?}", new_vec);
    });

    // 現在讓我們給 2030 年份加上一些資料
    tallinn.city_data(|x, y| { // 這次我們只稱呼輸入為 x 和 y
        x.push(2030);
        y.push(500_000);
    });

    // 我們不再想要 1834 的資料
    tallinn.city_data(|x, y| {
        let position_option = x.iter().position(|x| *x == 1834);
        if let Some(position) = position_option {
            println!(
                "Going to delete {} at position {:?} now.",
                x[position], position
            ); // 確認我們刪除了對的元素
            x.remove(position);
            y.remove(position);
        }
    });

    println!(
        "Years left are {:?}\nPopulations left are {:?}",
        tallinn.years, tallinn.populations
    );
}

印出一直以來我們呼叫 .city_data() 的結果。就是:

[(1372, 3250), (1834, 15300), (1851, 24000), (1881, 45900), (1897, 58800)]
Going to delete 1834 at position 1 now.
Years left are [1372, 1851, 1881, 1897, 1925, 1959, 1989, 2000, 2005, 2010, 2020, 2030]
Populations left are [3250, 24000, 45900, 58800, 119800, 283071, 478974, 400378, 401694, 406703, 437619, 500000]