Мы знаем, что параметры функции всегда имеют определение типа, чтобы указать тип - функция, мы используем обобщенный тип (generic), с требованием, что он должен имплементировать функциональную характеристику Fn, FnMut или FnOnce и указать сигнатуру функции например, Fn(i32)->i32, где в скобках указан тип параметра или параметров через запятую, а после -> указывается тип возвращаемого значения. На первый взгляд все немного сложно, это из-за правил владения ресурсами. Функциональные характеристики описывают правила доступа к окружению из замыканий. Для функций это не требуется, так как они не имеют доступа к окружению. Fn обозначает многократный не мутирующий доступ к значению, например по не мутирующей ссылке (&). FnOnce обозначает один мутирующий или один не мутирующий доступ к значению, например по мутирующей или не мутирующей ссылке (& или &mut). FnMut обозначает один мутирующий доступ к значению, например по мутирующей ссылке (&mut) и неявно включает FnOnce. Тема, конечно, не простая, но мы не будем углубляться и поговорим немного о другом.
Передача функции в качестве параметра (аргумента) другой функции имеет некоторые недостатки. Например, часто определенное действие требуется только один раз, но мы должны завернуть его в функцию, чья задача быть востребованной многократно, ситуация усугубляется при необходимости множества одноразовых действий. Проблема знакомая и нам на помощь приходят безымянные функции, которые мы можем создавать прямо в месте вызова. Из тела такой функции было бы очень удобно, получить доступ к переменным, находящимся в том же пространстве имен, т. е. к окружению (outside variables) функции. Про управление доступом к окружению мы говорили выше. Такие функции есть, они называются замыкания и имеют специальный синтаксис, который должен соответствовать функциональному типу (сигнатуре функции). Например, |x:i32|{x} соответствует сигнатуре (i32)->i32. Как мы видим Rust может вывести возвращаемый тип и его можно не указывать или указать явно |x:i32|->i32{x}. В примере показаны различные варианты использования.
//trait bounds for closures - access to outside variables
//1.Fn - multiple immutable access to value, can borrow as & (immutable reference)
fn higher_order_1(f: impl Fn(u32) -> u32) -> i32 {
let x = f(5);
x as i32
}
//2.FnMut - one mutable access to value, can borrow as &mut (mutable reference), implicit FnOnce
fn higher_order_2<F>(mut f: F) -> i32 where F: FnMut(u32) -> i32 {
let x = f(5);
x as i32 //
}
//3.FnOnce - one mut or immut access to value, can borrow as & (immut ref) or &mut (mut ref)
fn higher_order_3<F: FnOnce(u32) -> u32>(f: F) -> i32 {
let x = f(5);
x as i32
}
//simple function
fn foo_1(x: u32) -> u32 {
x + x
}
//can't use outside
fn foo_2(x: u32) -> i32 {
//inside string
let mut str_x = "x".to_string();
str_x.push('X');
println!("in the closure, str_x is now {}", str_x);
(x + x) as i32
} //inside string deleted
fn main() {
//outside string
let mut str_y = "y".to_string();
println!("higher_order_1: {:?}\n", higher_order_1(foo_1)); //Fn
println!("higher_order_2: {:?}\n", higher_order_2(foo_2)); //FnMut
//Closure
println!("higher_order_3: {:?}", higher_order_3( //FnOnce
|x: u32| {
str_y.push('Y'); //change outside string
println!("in the closure, str_y is now {}", str_y);
x
}
)); //outside string is alive
println!("after higher_order_3, str_y is {}", str_y);
}
В стандартной библиотеке Rust имеется множество функций, которые принимают в качестве своих аргументов замыкания, например sort_by(), которая принимает замыкание с сигнатурой (&T, &T) -> Ordering. Перечисление Ordering состоит из вариантов Greater, Less и Equal. Эта функция работает с коллекциями, принимая на вход два соседних элемента по очереди, сравнивает их и сортирует. В замыкании мы можем сами определять способ сравнения, если это обычное больше-меньше, то можно использовать стандартную для этой цели функцию cmp(). Замечу, что если замыкание состоит из одного выражения, то его тело можно не заключать в блок (фигурные скобки). Смотрим пример.
use std::cmp::Ordering;
fn cmp1(a: &i32, b: &i32) -> Ordering {
if a < b {
Ordering::Greater
} else if a > b {
Ordering::Less
} else {
Ordering::Equal
}
}
fn cmp2(a: &i32, b: &i32) -> Ordering {
a.cmp(b)
}
fn main() {
let mut arr = [4, 8, 1, 10, 0, 45, 12, 7];
arr.sort_by(cmp1);
println!("{:?}", arr); //[45, 12, 10, 8, 7, 4, 1, 0]
arr.sort_by(cmp2);
println!("{:?}", arr); //[0, 1, 4, 7, 8, 10, 12, 45]
arr.sort_by(|a, b| b.cmp(a));
println!("{:?}", arr); //[45, 12, 10, 8, 7, 4, 1, 0]
}
Перегуд В.
Комментариев нет:
Отправить комментарий