Closures

Closure

  • Замыкания можно присвоить переменным и вызывать;
  • В отличие от функций, замыкания могут использовать переменные вне своего блока.
(0..3).for_each(|x| {println!("map i = {}", x * 2);});

Map и Filter

.map(<closure>) передаёт владение элементами итератора замыканию, чтобы их можно было трансформировать в другие элементы, которые далее возвращает замыкание.

.filter(<closure>) возвращает оригинальные элементы, когда предикат замыкания возвращает true. Таким образом, отдавать владение элементами замыканию нельзя, и нужно передавать по ссылке.

let a = (0..3).map(|x| x * 2);
for i in a {
    println!("map i = {}", i);
}

let a = (0..3).filter(|&x| x % 2 == 0);
for i in a {
    println!("filter i = {}", i);
}

Вложенные замыкания map()

    let a = (0..=3).map(|x| x * 2).map(|y| y - 1);
    // первая итерация map(): 2, 4, 6
    // вторая итерация map(): 1, 3, 5
    for i in a {
        println!("{i}");
    }

Fold

Каждая итерация fold принимает 2 аргумента:

  • Исходное значение счётчика;
  • Замыкание, которое тоже принимает 2 аргумента: счётчик и объект. Замыкание возвращает счётчик.

fold используется для превращения множества в 1 значение. Пример для получения суммы всех чётных чисел:

pub fn main() {
    let even_sum = (1..=10).fold(0, |acc, num| if num % 2 == 0 { acc + num } else { acc });
    println!("{even_sum:?}");
}

fold можно часто заменить другими методами. Например, код выше можно заменить на отбор всех чётных числе с помощью filter и их сложение с помощью sum:

(0..=10).filter(|n| *n % 2 == 0).sum()

Наравне с sum также популярны product и collect.

Счётчик у fold необязательно числовой, можно использовать, например, String:

pub fn giant_grunts(initial: char) -> String {
    ["Bee", "Fee", "Gee", "Fi", "Hi", "Fo", "Mo", "Fum", "Tum"].iter().fold(
        String::new(),
        |acc, grunt| if grunt.starts_with(initial) { acc + grunt } else { acc },
    )}

pub fn main() {
    let song = giant_grunts('F');
    println!("{song:?}"); // "FeeFiFoFum" 
}

All

Замыкание all возвращает True, если все элементы в замыкании соответствуют условию.

let a: Vec<i32> = vec![1, 2, 3, 4];
print!("{}\n", a.into_iter().all(|x| x > 1)); // false

Для пустого вектора замыкание all вернёт True:

let a: Vec<i32> = vec![];
print!("{}\n", a.into_iter().all(|x| x > 1)); // true

Цикл через замыкание vs for

use std::collections::HashMap;
pub fn main() {
    let num_vec = vec![1, 2, 1, 3, 5, 2, 1, 4, 6];
    let mut number_count: HashMap<i32, i32> = HashMap::new();
    for key in num_vec {
        *number_count.entry(key).or_default() += 1;
    }
    /* for (k, v) in number_count {
        print!("{} -> {}; ", k, v);
    } */
    
    number_count.iter().for_each(|(k, v)| {
        print!("{} -> {}; ", k, v);
    }); //цикл через замыкание итератора
}