函式中的閉包
閉包超棒的。那麼我們要如何把它們放到我們擁有的函式中呢?
你可以寫你自己的函式來接受閉包,但是在它裡面就沒那麼自由了,你必須決定型別。在函式外的閉包可以在 Fn
、FnMut
和 FnOnce
之間自行決定,但在函式內部你必須選擇其中一種。最好的理解方式是多看幾個函式簽名。這裡是其中的 .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
就是閉包:這是變數名和型別。當然,f
和 F
並沒有什麼特別之處,它們可以是不同的名字。如果想要你也可以寫成 my_closure: Closure
──這並不要緊。但在簽名中,你幾乎總是會看到 f: F
。
接下來是關於閉包的部分:F: FnMut(Self::Item) -> bool
。在這裡它決定閉包型別是 FnMut
,所以它可以改變值。它改變了它所接受的疊代器 Self::Item
的值。而且它必須回傳 true
或 false
。
這裡是個更簡單帶有閉包的簽名:
#![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]