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);
}); //цикл через замыкание итератора
}