函数中的闭包
闭包是伟大的。那么我们如何把它们放到自己的函数中呢?
你可以创建自己的函数来接受闭包,但是在函数里面就不那么自由了,你必须决定类型。在函数外部,一个闭包可以在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) // We bring in self, but only f is generic F. f is the closure where F: FnMut(&mut Vec<u32>, &mut Vec<u32>), // The closure takes mutable vectors of u32 // which are the year and population data { f(&mut self.years, &mut self.populations) // Finally this is the actual function. It says // "use a closure on self.years and self.populations" // We can do whatever we want with the closure } } 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, ]; // Now we can create our city let mut tallinn = City::new("Tallinn", years, populations); // Now we have a .city_data() method that has a closure. We can do anything we want. // First let's put the data for 5 years together and print it. tallinn.city_data(|city_years, city_populations| { // We can call the input anything we want let new_vec = city_years .into_iter() .zip(city_populations.into_iter()) // Zip the two together .take(5) // but only take the first 5 .collect::<Vec<(_, _)>>(); // Tell Rust to decide the type inside the tuple println!("{:?}", new_vec); }); // Now let's add some data for the year 2030 tallinn.city_data(|x, y| { // This time we just call the input x and y x.push(2030); y.push(500_000); }); // We don't want the 1834 data anymore 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 ); // Confirm that we delete the right item 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]