Closures in functions

Closures are great. So how do we put them into our own functions?

You can make your own functions that take closures, but inside them it is less free and you have to decide the type. Outside a function a closure can decide by itself between Fn, FnMut and FnOnce, but inside you have to choose one. The best way to understand is to look at a few function signatures. Here is the one for .all(). We remember that it checks an iterator to see if everything is true (depending on what you decide is true or false). Part of its signature says this:


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

fn all<F>: this tells you that there is a generic type F. A closure is always generic because every time it is a different type.

(&mut self, f: F): &mut self tells you that it's a method. f: F is usually what you see for a closure: this is the variable name and the type. Of course, there is nothing special about f and F and they could be different names. You could write my_closure: Closure if you wanted - it doesn't matter. But in signatures you almost always see f: F.

Next is the part about the closure: F: FnMut(Self::Item) -> bool. Here it decides that the closure is FnMut, so it can change the values. It changes the values of Self::Item, which is the iterator that it takes. And it has to return true or false.

Here is a much simpler signature with a closure:


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

This just says that it takes a closure, takes the value (FnOnce = takes the value), and doesn't return anything. So now we can call this closure that takes nothing and do whatever we like. We will create a Vec and then iterate over it just to show what we can do now.

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));
    })
}

For a more real example, we will create a City struct again. This time the City struct has more data about years and populations. It has a Vec<u32> for all the years, and another Vec<u32> for all the populations.

City has two functions: new() to create a new City, and .city_data() which has a closure. When we use .city_data(), it gives us the years and the populations and a closure, so we can do what we want with the data. The closure type is FnMut so we can change the data. It looks like this:

#[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
    );
}

This will print the result of all the times we called .city_data(). It is:

[(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]