Замыкания — одна из основных концепций и самых элегантных особенностей функционального программирования. Испытайте магию замыканий в Топазе! ✨
🌱 Основные концепции замыканий
Что такое замыкания?
Замыкание — это функция, которая может запоминать и обращаться к переменным из окружения, где она была определена. Это называется замыканием, потому что функция "замыкается" над окружением (областью видимости) из момента своего создания.
// Базовый пример замыкания
function внешняяФункция(внешняяПеременная: int) -> impl Fn(int) -> int {
// Создание замыкания - захватывает внешнююПеременную
let внутренняяФункция = |внутренняяПеременная: int| -> int {
внешняяПеременная + внутренняяПеременная // Обращение к переменной из внешней области
}
return внутренняяФункция
}
// Пример счётчика с замыканием
function создатьСчётчик(начальноеЗначение: int) -> impl Fn() -> int {
let mut текущееЗначение = начальноеЗначение
// Замыкание, захватывающее текущееЗначение
return move || -> int {
текущееЗначение += 1
текущееЗначение
}
}
// Генератор функций умножения
function создатьУмножитель(множитель: int) -> impl Fn(int) -> int {
|значение: int| значение * множитель
} test {
// Тест внешней функции
let прибавить5 = внешняяФункция(5)
assert прибавить5(3) == 8
assert прибавить5(10) == 15
// Тест счётчика
let mut счётчик1 = создатьСчётчик(0)
let mut счётчик2 = создатьСчётчик(100)
assert счётчик1() == 1
assert счётчик1() == 2
assert счётчик2() == 101
assert счётчик1() == 3 // Независимое состояние сохраняется
// Тест генератора умножителя
let удвоитель = создатьУмножитель(2)
let утроитель = создатьУмножитель(3)
assert удвоитель(5) == 10
assert утроитель(4) == 12
}
print("прибавить5(7) = {внешняяФункция(5)(7)}")
let mut мойСчётчик = создатьСчётчик(10)
print("Счётчик: {мойСчётчик()}, {мойСчётчик()}, {мойСчётчик()}")
let десятикратныйУмножитель = создатьУмножитель(10)
print("10 * 7 = {десятикратныйУмножитель(7)}")
Основные характеристики замыканий
- Захват среды: Запоминает переменные из внешних областей видимости
- Сохранение состояния: Поддерживает состояние между вызовами функций
- Отложенное выполнение: Откладывает вычисления до необходимости
- Повторное использование кода: Одна логика применима в разных контекстах
🎨 Различные паттерны замыканий
Захват по значению vs захват по ссылке
// Захват по значению (Copy)
function примерЗахватаЗначения() -> impl Fn() -> int {
let значение = 42
// Значение копируется в замыкание
|| значение // Значение копируется внутрь замыкания
}
// Захват по ссылке (Borrow)
function примерЗахватаСсылки() -> impl Fn() -> int {
let значение = 42
// Захват по ссылке (когда время жизни позволяет)
move || значение // Передача владения ключевым словом move
}
// Захват изменяемой ссылки
function примерИзменяемогоЗахвата() -> impl FnMut() -> int {
let mut счётчик = 0
// Захват изменяемой ссылки
move || -> int {
счётчик += 1
счётчик
}
}
// Захват сложной среды
function сложнаяСреда() -> (impl Fn(int) -> int, impl Fn(int) -> int) {
let базовоеЗначение = 10
let множитель = 3
let mut накопленноеЗначение = 0
let функцияСложения = move |дополнительноеЗначение: int| -> int {
накопленноеЗначение += дополнительноеЗначение
базовоеЗначение + накопленноеЗначение
}
let функцияУмножения = |входноеЗначение: int| -> int {
входноеЗначение * множитель
}
return (функцияСложения, функцияУмножения)
}
// Цепочка замыканий
function создатьЦепочкуФункций(начальнаяФункция: impl Fn(int) -> int) -> impl Fn(impl Fn(int) -> int) -> impl Fn(int) -> int {
move |следующаяФункция: impl Fn(int) -> int| -> impl Fn(int) -> int {
move |вход: int| -> int {
следующаяФункция(начальнаяФункция(вход))
}
}
} test {
// Тест захвата значения
let замыканиеЗначения = примерЗахватаЗначения()
assert замыканиеЗначения() == 42
// Тест изменяемого захвата
let mut изменяемоеЗамыкание = примерИзменяемогоЗахвата()
assert изменяемоеЗамыкание() == 1
assert изменяемоеЗамыкание() == 2
assert изменяемоеЗамыкание() == 3
// Тест сложной среды
let (mut функцияСложения, функцияУмножения) = сложнаяСреда()
assert функцияСложения(5) == 15 // 10 + 5
assert функцияСложения(3) == 18 // 10 + 5 + 3
assert функцияУмножения(4) == 12 // 4 * 3
// Тест цепочки функций
let удвоить = |x: int| x * 2
let прибавить10 = |x: int| x + 10
let цепнаяФункция = создатьЦепочкуФункций(удвоить)(прибавить10)
assert цепнаяФункция(5) == 20 // (5 * 2) + 10
}
let mut моёИзменяемоеЗамыкание = примерИзменяемогоЗахвата()
print("Изменяемое замыкание: {моёИзменяемоеЗамыкание()}, {моёИзменяемоеЗамыкание()}")
let (mut функцияСложения, функцияУмножения) = сложнаяСреда()
print("Сложение: {функцияСложения(7)}, Умножение: {функцияУмножения(6)}")
Функции высшего порядка и замыкания
// Функция, возвращающая функцию
function создательУсловныхФункций(условие: bool) -> impl Fn(int) -> int {
if условие {
|x: int| x * 2 // Удвоить
} else {
|x: int| x + 10 // Прибавить 10
}
}
// Функция, принимающая функцию как параметр
function применительФункций<F, T>(значение: T, функция: F) -> T
where
F: Fn(T) -> T
{
функция(значение)
}
// Композитор, объединяющий несколько функций
function композиторФункций<F, G, T>(функция1: F, функция2: G) -> impl Fn(T) -> T
where
F: Fn(T) -> T,
G: Fn(T) -> T,
T: 'static
{
move |вход: T| -> T {
функция2(функция1(вход))
}
}
// Реализация каррирования
function примерКаррирования(а: int) -> impl Fn(int) -> impl Fn(int) -> int {
move |б: int| -> impl Fn(int) -> int {
move |в: int| -> int {
а + б + в
}
}
}
// Замыкание мемоизации
function мемоизация<F, T, U>(функция: F) -> impl FnMut(T) -> U
where
F: Fn(T) -> U,
T: Clone + std::hash::Hash + Eq,
U: Clone,
{
use std::collections::HashMap
let mut кэш: HashMap<T, U> = HashMap::new()
move |вход: T| -> U {
match кэш.get(&вход) { case Some(результат) => return результат.clone(), case None => {} }
let результат = функция(вход.clone())
кэш.insert(вход, результат.clone())
результат
}
} test {
// Тест создателя условных функций
let функцияУдвоения = создательУсловныхФункций(true)
let функцияСложения = создательУсловныхФункций(false)
assert функцияУдвоения(5) == 10
assert функцияСложения(5) == 15
// Тест применителя функций
let результат = применительФункций(10, |x| x * 3)
assert результат == 30
// Тест композитора функций
let прибавить5 = |x: int| x + 5
let умножить2 = |x: int| x * 2
let составнаяФункция = композиторФункций(прибавить5, умножить2)
assert составнаяФункция(3) == 16 // (3 + 5) * 2
// Тест каррирования
let каррированнаяФункция = примерКаррирования(1)(2)(3)
assert каррированнаяФункция == 6 // 1 + 2 + 3
// Тест мемоизации
let mut мемоФакториал = мемоизация(|н: int| -> int {
if н <= 1 { 1 } else { н * мемоФакториал(н - 1) }
})
assert мемоФакториал(5) == 120
}
print("Условная(true): {создательУсловныхФункций(true)(7)}")
print("Результат составной функции: {композиторФункций(|x| x + 1, |x| x * 10)(4)}")
print("Результат каррирования: {примерКаррирования(10)(20)(30)}")
Обработка событий и колбэки
// Определение типа обработчика событий
type ОбработчикСобытий<T> = Box<dyn FnMut(T)>
// Простая система событий
struct СистемаСобытий<T> {
обработчики: Vec<ОбработчикСобытий<T>>
}
impl<T> СистемаСобытий<T>
where
T: Clone
{
function new() -> Self {
СистемаСобытий {
обработчики: Vec::new()
}
}
// Регистрация обработчика событий
function зарегистрироватьОбработчик<F>(&mut self, обработчик: F)
where
F: FnMut(T) + 'static
{
self.обработчики.push(Box::new(обработчик))
}
// Запуск события
function запуститьСобытие(&mut self, данные: T) {
for обработчик in &mut self.обработчики {
обработчик(данные.clone())
}
}
}
// Симуляция асинхронной операции
function асинхроннаяОперация<F>(задержкаМс: int, колбэкЗавершения: F)
where
F: FnOnce(string) + 'static
{
// В реальности это было бы асинхронно, но здесь выполняем немедленно
let результат = format!("Задача завершена (через {}мс)", задержкаМс)
колбэкЗавершения(результат)
}
// Детектор изменения состояния
struct НаблюдательСостояния<T> {
текущееСостояние: T,
колбэкиИзменений: Vec<Box<dyn Fn(&T, &T)>>
}
impl<T> НаблюдательСостояния<T>
where
T: Clone + PartialEq
{
function new(начальноеСостояние: T) -> Self {
НаблюдательСостояния {
текущееСостояние: начальноеСостояние,
колбэкиИзменений: Vec::new()
}
}
function зарегистрироватьКолбэкИзменения<F>(&mut self, колбэк: F)
where
F: Fn(&T, &T) + 'static
{
self.колбэкиИзменений.push(Box::new(колбэк))
}
function изменитьСостояние(&mut self, новоеСостояние: T) {
if self.текущееСостояние != новоеСостояние {
let предыдущееСостояние = self.текущееСостояние.clone()
self.текущееСостояние = новоеСостояние.clone()
for колбэк in &self.колбэкиИзменений {
колбэк(&предыдущееСостояние, &новоеСостояние)
}
}
}
function текущееЗначение(&self) -> &T {
&self.текущееСостояние
}
} test {
// Тест системы событий
let mut системаСобытий = СистемаСобытий::<string>::new()
let mut полученныеСообщения: Vec<string> = Vec::new()
системаСобытий.зарегистрироватьОбработчик(move |сообщение: string| {
полученныеСообщения.push(format!("Обработчик1: {}", сообщение))
})
системаСобытий.зарегистрироватьОбработчик(|сообщение: string| {
println!("Обработчик2 получил: {}", сообщение)
})
системаСобытий.запуститьСобытие("Тестовое сообщение".to_string())
// Тест наблюдателя состояния
let mut наблюдательТемпературы = НаблюдательСостояния::new(20.0)
let mut журналИзменений: Vec<string> = Vec::new()
наблюдательТемпературы.зарегистрироватьКолбэкИзменения(move |предыдущее: &f64, новоеЗначение: &f64| {
журналИзменений.push(format!("Изменение температуры: {}°C → {}°C", предыдущее, новоеЗначение))
})
наблюдательТемпературы.изменитьСостояние(25.0)
наблюдательТемпературы.изменитьСостояние(30.0)
assert *наблюдательТемпературы.текущееЗначение() == 30.0
}
// Пример использования
let mut событиеКлика = СистемаСобытий::<string>::new()
событиеКлика.зарегистрироватьОбработчик(|имяКнопки: string| {
print!("Кнопка '{}' была нажата!", имяКнопки)
})
событиеКлика.запуститьСобытие("ОК".to_string())
// Пример асинхронной операции
асинхроннаяОперация(1000, |результат: string| {
print!("Результат асинхронной операции: {}", результат)
})
🚀 Практические применения замыканий
Паттерны функционального программирования
// Функциональный конвейер
function конвейер<T>(начальноеЗначение: T) -> СтроительКонвейера<T> {
СтроительКонвейера::new(начальноеЗначение)
}
struct СтроительКонвейера<T> {
значение: T
}
impl<T> СтроительКонвейера<T> {
function new(значение: T) -> Self {
СтроительКонвейера { значение }
}
function map<U, F>(self, функция: F) -> СтроительКонвейера<U>
where
F: FnOnce(T) -> U
{
СтроительКонвейера::new(функция(self.значение))
}
function filter<F>(self, условие: F) -> Option<СтроительКонвейера<T>>
where
F: FnOnce(&T) -> bool
{
if условие(&self.значение) {
Some(self)
} else {
None
}
}
function fold<U, F>(self, начальноеЗначение: U, функция: F) -> U
where
F: FnOnce(U, T) -> U
{
функция(начальноеЗначение, self.значение)
}
function finish(self) -> T {
self.значение
}
}
// Утилита обработки массивов
function процессорМассивов<T>() -> ПроцессорМассивов<T> {
ПроцессорМассивов::new()
}
struct ПроцессорМассивов<T> {
_phantom: std::marker::PhantomData<T>
}
impl<T> ПроцессорМассивов<T> {
function new() -> Self {
ПроцессорМассивов { _phantom: std::marker::PhantomData }
}
function map<U, F>(&self, массив: Vec<T>, функция: F) -> Vec<U>
where
F: Fn(T) -> U
{
массив.into_iter().map(функция).collect()
}
function filter<F>(&self, массив: Vec<T>, условие: F) -> Vec<T>
where
F: Fn(&T) -> bool
{
массив.into_iter().filter(условие).collect()
}
function reduce<F>(&self, массив: Vec<T>, функция: F) -> Option<T>
where
F: Fn(T, T) -> T
{
массив.into_iter().reduce(функция)
}
function fold<U, F>(&self, массив: Vec<T>, начальноеЗначение: U, функция: F) -> U
where
F: Fn(U, T) -> U
{
массив.into_iter().fold(начальноеЗначение, функция)
}
}
// Цепочка условного выполнения
function еслиУсловие<F>(условие: bool, функцияВыполнения: F) -> УсловноеВыполнение<F>
where
F: FnOnce()
{
УсловноеВыполнение { условие, функцияВыполнения: Some(функцияВыполнения) }
}
struct УсловноеВыполнение<F>
where
F: FnOnce()
{
условие: bool,
функцияВыполнения: Option<F>
}
impl<F> УсловноеВыполнение<F>
where
F: FnOnce()
{
function иначе<G>(mut self, функцияИначе: G)
where
G: FnOnce()
{
if self.условие {
match self.функцияВыполнения.take() { case Some(функция) => функция(), case None => {} }
} else {
функцияИначе()
}
}
function выполнить(mut self) {
if self.условие {
match self.функцияВыполнения.take() { case Some(функция) => функция(), case None => {} }
}
}
} test {
// Тест конвейера
let результат = конвейер(10)
.map(|x| x * 2)
.map(|x| x + 5)
.filter(|&x| x > 20)
.map(|x| x.to_string())
.finish()
assert результат == Some("25".to_string())
// Тест процессора массивов
let процессорМассивов = процессорМассивов::<i32>()
let числа = vec![1, 2, 3, 4, 5]
let квадраты = процессорМассивов.map(числа.clone(), |x| x * x)
assert квадраты == vec![1, 4, 9, 16, 25]
let чётные = процессорМассивов.filter(числа.clone(), |&x| x % 2 == 0)
assert чётные == vec![2, 4]
let сумма = процессорМассивов.fold(числа, 0, |акк, x| акк + x)
assert сумма == 15
// Тест условного выполнения
let mut выполнено = false
еслиУсловие(true, || {
выполнено = true
}).выполнить()
assert выполнено == true
}
// Пример использования
let результатОбработки = конвейер(vec![1, 2, 3, 4, 5])
.map(|массив| процессорМассивов::<i32>().map(массив, |x| x * 2))
.map(|массив| процессорМассивов::<i32>().filter(массив, |&x| x > 5))
.finish()
print!("Обработанный массив: {:?}", результатОбработки)
еслиУсловие(результатОбработки.len() > 2, || {
print!("Массив достаточно большой!")
}).иначе(|| {
print!("Массив слишком мал.")
})
Ленивое вычисление и потоки
// Последовательность ленивого вычисления
struct ЛениваяПоследовательность<T, F>
where
F: Fn() -> Option<T>
{
функцияГенератор: F
}
impl<T, F> ЛениваяПоследовательность<T, F>
where
F: Fn() -> Option<T>
{
function new(функцияГенератор: F) -> Self {
ЛениваяПоследовательность { функцияГенератор }
}
function take(self, количество: usize) -> Vec<T> {
let mut результат = Vec::new()
for _ in 0..количество {
match (self.функцияГенератор)() { case Some(значение) => результат.push(значение), case None => break }
}
результат
}
}
// Генератор бесконечной последовательности
function бесконечнаяПоследовательность(начало: int, шаг: int) -> impl Fn() -> Option<int> {
let mut текущее = начало
move || -> Option<int> {
let значение = текущее
текущее += шаг
Some(значение)
}
}
// Генератор последовательности Фибоначчи
function генераторФибоначчи() -> impl Fn() -> Option<int> {
let mut а = 0
let mut б = 1
move || -> Option<int> {
let текущее = а
let следующее = а + б
а = б
б = следующее
Some(текущее)
}
}
// Условная бесконечная последовательность
function условнаяПоследовательность<F, P>(функцияГенератор: F, условие: P) -> impl Fn() -> Option<int>
where
F: Fn() -> int,
P: Fn(int) -> bool
{
move || -> Option<int> {
loop {
let значение = функцияГенератор()
if условие(значение) {
return Some(значение)
}
}
}
}
// Процессор потоков
struct Поток<T> {
данные: Vec<T>
}
impl<T> Поток<T>
where
T: Clone
{
function from(данные: Vec<T>) -> Self {
Поток { данные }
}
function map<U, F>(self, функция: F) -> Поток<U>
where
F: Fn(T) -> U
{
Поток {
данные: self.данные.into_iter().map(функция).collect()
}
}
function filter<F>(self, условие: F) -> Поток<T>
where
F: Fn(&T) -> bool
{
Поток {
данные: self.данные.into_iter().filter(условие).collect()
}
}
function take(self, количество: usize) -> Поток<T> {
Поток {
данные: self.данные.into_iter().take(количество).collect()
}
}
function collect(self) -> Vec<T> {
self.данные
}
} test {
// Тест бесконечной последовательности
let арифметическаяПоследовательность = бесконечнаяПоследовательность(1, 2) // 1, 3, 5, 7, ...
let первыеПять = (0..5).map(|_| арифметическаяПоследовательность()).collect::<Vec<_>>()
assert первыеПять == vec![Some(1), Some(3), Some(5), Some(7), Some(9)]
// Тест Фибоначчи
let фибоначчи = генераторФибоначчи()
let первыеШестьФибоначчи: Vec<_> = (0..6).map(|_| фибоначчи()).collect()
assert первыеШестьФибоначчи == vec![Some(0), Some(1), Some(1), Some(2), Some(3), Some(5)]
// Тест обработки потоков
let результат = Поток::from(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
.filter(|&x| x % 2 == 0) // Только чётные числа
.map(|x| x * x) // Квадрат
.take(3) // Первые 3
.collect()
assert результат == vec![4, 16, 36] // 2², 4², 6²
}
// Пример использования
let генераторНечётных = бесконечнаяПоследовательность(1, 2)
print!("Первые 10 нечётных чисел: {:?}", (0..<10).map(|_| генераторНечётных()).collect::<Vec<_>>())
let фибоначчи = генераторФибоначчи()
print!("Последовательность Фибоначчи: {:?}", (0..8).map(|_| фибоначчи()).collect::<Vec<_>>())
let результатПотока = Поток::from((1..=20).collect())
.filter(|&x| x % 3 == 0) // Кратные 3
.map(|x| format!("{}-й", x)) // Преобразование в строку
.take(4)
.collect()
print!("Кратные 3: {:?}", результатПотока)
🎯 Продвинутые техники замыканий
Динамическое создание функций
// Генератор функций во время выполнения
enum ТипОперации {
Сложение,
Вычитание,
Умножение,
Деление
}
function создатьФункциюОперации(операция: ТипОперации, операнд: f64) -> Box<dyn Fn(f64) -> f64> {
match операция {
case ТипОперации::Сложение => Box::new(move |x| x + операнд),
case ТипОперации::Вычитание => Box::new(move |x| x - операнд),
case ТипОперации::Умножение => Box::new(move |x| x * операнд),
case ТипОперации::Деление => Box::new(move |x| {
if операнд != 0.0 { x / операнд } else { f64::NAN }
})
}
}
// Условная цепочка функций
struct ЦепочкаФункций<T> {
функции: Vec<Box<dyn Fn(T) -> T>>
}
impl<T> ЦепочкаФункций<T>
where
T: 'static
{
function new() -> Self {
ЦепочкаФункций { функции: Vec::new() }
}
function добавить<F>(mut self, функция: F) -> Self
where
F: Fn(T) -> T + 'static
{
self.функции.push(Box::new(функция))
self
}
function условноДобавить<F>(mut self, условие: bool, функция: F) -> Self
where
F: Fn(T) -> T + 'static
{
if условие {
self.функции.push(Box::new(функция))
}
self
}
function выполнить(self, начальноеЗначение: T) -> T {
self.функции.into_iter().fold(начальноеЗначение, |значение, функция| функция(значение))
}
}
// Настраиваемый генератор валидаторов
struct ПравилоВалидации<T> {
имя: string,
функцияВалидации: Box<dyn Fn(&T) -> bool>
}
struct Валидатор<T> {
правила: Vec<ПравилоВалидации<T>>
}
impl<T> Валидатор<T> {
function new() -> Self {
Валидатор { правила: Vec::new() }
}
function добавитьПравило<F>(mut self, имя: string, функцияВалидации: F) -> Self
where
F: Fn(&T) -> bool + 'static
{
self.правила.push(ПравилоВалидации {
имя,
функцияВалидации: Box::new(функцияВалидации)
})
self
}
function валидировать(&self, значение: &T) -> Result<(), Vec<string>> {
let mut ошибки = Vec::new()
for правило in &self.правила {
if !(правило.функцияВалидации)(значение) {
ошибки.push(правило.имя.clone())
}
}
if ошибки.is_empty() {
Ok(())
} else {
Err(ошибки)
}
}
} test {
// Тест динамического создания функций
let прибавить10 = создатьФункциюОперации(ТипОперации::Сложение, 10.0)
let умножить2 = создатьФункциюОперации(ТипОперации::Умножение, 2.0)
assert прибавить10(5.0) == 15.0
assert умножить2(7.0) == 14.0
// Тест цепочки функций
let цепочка = ЦепочкаФункций::new()
.добавить(|x: i32| x + 1)
.условноДобавить(true, |x: i32| x * 2)
.добавить(|x: i32| x - 3)
let результат = цепочка.выполнить(5) // ((5 + 1) * 2) - 3 = 9
assert результат == 9
// Тест валидатора
let валидаторЧисел = Валидатор::new()
.добавитьПравило("должно быть положительным".to_string(), |&x: &i32| x > 0)
.добавитьПравило("должно быть меньше 100".to_string(), |&x: &i32| x < 100)
.добавитьПравило("должно быть чётным".to_string(), |&x: &i32| x % 2 == 0)
assert валидаторЧисел.валидировать(&50).is_ok()
assert валидаторЧисел.валидировать(&-5).is_err()
assert валидаторЧисел.валидировать(&51).is_err() // Не проходит, потому что нечётное
}
// Пример использования
let цепочкаВычислений = ЦепочкаФункций::new()
.добавить(|x: f64| x * 1.5) // Увеличение на 50%
.условноДобавить(true, |x| x + 10.0) // Прибавить 10
.добавить(|x| x.round()) // Округлить
let финальныйРезультат = цепочкаВычислений.выполнить(7.3)
print!("Результат вычисления: {}", финальныйРезультат)
let валидаторСтрок = Валидатор::new()
.добавитьПравило("не пустая".to_string(), |s: &string| !s.is_empty())
.добавитьПравило("минимальная длина".to_string(), |s: &string| s.len() >= 3)
.добавитьПравило("содержит спецсимвол".to_string(), |s: &string| s.contains('@'))
match валидаторСтрок.валидировать(&"test@example.com".to_string()) {
case Ok(()) => print!("Валидация успешна!"),
case Err(ошибки) => print!("Валидация не пройдена: {:?}", ошибки)
}
Система плагинов
// Интерфейс плагина
trait Плагин {
function имя(&self) -> &str
function выполнить(&self, вход: &str) -> string
}
// Плагин на основе замыкания
struct ПлагинЗамыкание<F>
where
F: Fn(&str) -> string
{
имя: string,
функцияВыполнения: F
}
impl<F> ПлагинЗамыкание<F>
where
F: Fn(&str) -> string
{
function new(имя: string, функцияВыполнения: F) -> Self {
ПлагинЗамыкание { имя, функцияВыполнения }
}
}
impl<F> Плагин for ПлагинЗамыкание<F>
where
F: Fn(&str) -> string
{
function имя(&self) -> &str {
&self.имя
}
function выполнить(&self, вход: &str) -> string {
(self.функцияВыполнения)(вход)
}
}
// Менеджер плагинов
struct МенеджерПлагинов {
плагины: Vec<Box<dyn Плагин>>
}
impl МенеджерПлагинов {
function new() -> Self {
МенеджерПлагинов { плагины: Vec::new() }
}
function зарегистрироватьПлагин<P>(mut self, плагин: P) -> Self
where
P: Плагин + 'static
{
self.плагины.push(Box::new(плагин))
self
}
function выполнитьВсеПлагины(&self, вход: &str) -> Vec<(string, string)> {
self.плагины
.iter()
.map(|плагин| (плагин.имя().to_string(), плагин.выполнить(вход)))
.collect()
}
function найтиПлагин(&self, имя: &str) -> Option<&dyn Плагин> {
self.плагины
.iter()
.find(|плагин| плагин.имя() == имя)
.map(|плагин| плагин.as_ref())
}
}
// Система промежуточного ПО
type ПромежуточноеПО<T> = Box<dyn Fn(T, Box<dyn Fn(T) -> T>) -> T>
struct ЦепочкаПромежуточногоПО<T> {
промежуточноеПО: Vec<ПромежуточноеПО<T>>
}
impl<T> ЦепочкаПромежуточногоПО<T>
where
T: 'static
{
function new() -> Self {
ЦепочкаПромежуточногоПО { промежуточноеПО: Vec::new() }
}
function добавитьПромежуточноеПО<F>(mut self, промежуточноеПО: F) -> Self
where
F: Fn(T, Box<dyn Fn(T) -> T>) -> T + 'static
{
self.промежуточноеПО.push(Box::new(промежуточноеПО))
self
}
function выполнить<F>(self, вход: T, финальныйОбработчик: F) -> T
where
F: Fn(T) -> T + 'static
{
let mut цепочка = Box::new(финальныйОбработчик) as Box<dyn Fn(T) -> T>
for промежуточноеПО in self.промежуточноеПО.into_iter().rev() {
let предыдущаяЦепочка = цепочка
цепочка = Box::new(move |вход| промежуточноеПО(вход, предыдущаяЦепочка))
}
цепочка(вход)
}
} test {
// Тест системы плагинов
let менеджер = МенеджерПлагинов::new()
.зарегистрироватьПлагин(ПлагинЗамыкание::new(
"верхний регистр".to_string(),
|текст| текст.to_uppercase()
))
.зарегистрироватьПлагин(ПлагинЗамыкание::new(
"счётчик длины".to_string(),
|текст| format!("Длина: {}", текст.len())
))
.зарегистрироватьПлагин(ПлагинЗамыкание::new(
"обратный порядок".to_string(),
|текст| текст.chars().rev().collect()
))
let результаты = менеджер.выполнитьВсеПлагины("привет мир")
assert результаты.len() == 3
match менеджер.найтиПлагин("верхний регистр") { case Some(плагинВерхнегоРегистра) => assert плагинВерхнегоРегистра.выполнить("тест") == "ТЕСТ", case None => {} }
// Тест промежуточного ПО
let цепочка = ЦепочкаПромежуточногоПО::new()
.добавитьПромежуточноеПО(|значение: i32, следующее| {
print!("Промежуточное ПО 1: вход {}", значение)
let результат = следующее(значение + 1)
print!("Промежуточное ПО 1: выход {}", результат)
результат
})
.добавитьПромежуточноеПО(|значение, следующее| {
print!("Промежуточное ПО 2: вход {}", значение)
let результат = следующее(значение * 2)
print!("Промежуточное ПО 2: выход {}", результат)
результат
})
let финальныйРезультат = цепочка.выполнить(5, |значение| {
print!("Финальный обработчик: {}", значение)
значение + 10
})
assert финальныйРезультат == 22 // ((5 + 1) * 2) + 10
}
// Пример использования
let менеджерОбработкиТекста = МенеджерПлагинов::new()
.зарегистрироватьПлагин(ПлагинЗамыкание::new(
"HTML экранирование".to_string(),
|текст| текст.replace("&", "&").replace("<", "<").replace(">", ">")
))
.зарегистрироватьПлагин(ПлагинЗамыкание::new(
"выделение markdown".to_string(),
|текст| format!("**{}**", текст)
))
let результатыОбработки = менеджерОбработкиТекста.выполнитьВсеПлагины("Привет <мир> & друзья!")
for (имяПлагина, результат) in результатыОбработки {
print!("{}: {}", имяПлагина, результат)
}
🔍 Оптимизация производительности замыканий
Эффективные по памяти замыкания
// Замыкание на основе ссылок (эффективно по памяти)
function замыканиеНаОснореСсылок(данные: &[i32]) -> impl Fn(usize) -> Option<i32> + '_ {
move |индекс| данные.get(индекс).copied()
}
// Использование умных указателей
use std::rc::Rc
use std::sync::Arc
function создатьОбщееЗамыкание(общиеДанные: Rc<Vec<i32>>) -> impl Fn(usize) -> Option<i32> {
move |индекс| общиеДанные.get(индекс).copied()
}
function создатьПотокобезопасноеЗамыкание(общиеДанные: Arc<Vec<i32>>) -> impl Fn(usize) -> Option<i32> + Send + Sync {
move |индекс| общиеДанные.get(индекс).copied()
}
// Ленивая инициализация замыкания
struct ЛениваяИнициализация<T, F>
where
F: FnOnce() -> T
{
функцияИнициализации: Option<F>,
значение: Option<T>
}
impl<T, F> ЛениваяИнициализация<T, F>
where
F: FnOnce() -> T
{
function new(функцияИнициализации: F) -> Self {
ЛениваяИнициализация {
функцияИнициализации: Some(функцияИнициализации),
значение: None
}
}
function get(&mut self) -> &T {
if self.значение.is_none() {
let функцияИнициализации = self.функцияИнициализации.take().unwrap()
self.значение = Some(функцияИнициализации())
}
self.значение.as_ref().unwrap()
}
}
// Пулинг замыканий
struct ПулЗамыканий<F, T, U>
where
F: Fn(T) -> U
{
функции: Vec<F>,
текущийИндекс: usize
}
impl<F, T, U> ПулЗамыканий<F, T, U>
where
F: Fn(T) -> U + Clone
{
function new(функция: F, размер: usize) -> Self {
ПулЗамыканий {
функции: vec![функция; размер],
текущийИндекс: 0
}
}
function выполнить(&mut self, вход: T) -> U {
let функция = &self.функции[self.текущийИндекс]
self.текущийИндекс = (self.текущийИндекс + 1) % self.функции.len()
функция(вход)
}
} test {
// Тест общего замыкания
let данные = Rc::new(vec![1, 2, 3, 4, 5])
let замыкание1 = создатьОбщееЗамыкание(данные.clone())
let замыкание2 = создатьОбщееЗамыкание(данные.clone())
assert замыкание1(0) == Some(1)
assert замыкание2(4) == Some(5)
// Тест ленивой инициализации
let mut ленивоеЗначение = ЛениваяИнициализация::new(|| {
print!("Выполнено дорогое вычисление")
42
})
assert *ленивоеЗначение.get() == 42
assert *ленивоеЗначение.get() == 42 // Нет вычисления при втором вызове
// Тест пула замыканий
let mut пул = ПулЗамыканий::new(|x: i32| x * 2, 3)
assert пул.выполнить(5) == 10
assert пул.выполнить(3) == 6
}
// Замыкание для измерения производительности
function бенчмарк<F, T>(имя: &str, функция: F, вход: T) -> T
where
F: FnOnce(T) -> T
{
use std::time::Instant
let времяНачала = Instant::now()
let результат = функция(вход)
let прошедшееВремя = времяНачала.elapsed()
print!("{} время выполнения: {:?}", имя, прошедшееВремя)
результат
}
// Кэшированное замыкание
use std::collections::HashMap
struct КэшированноеЗамыкание<F, K, V>
where
F: Fn(K) -> V,
K: Clone + std::hash::Hash + Eq,
V: Clone
{
функция: F,
кэш: HashMap<K, V>
}
impl<F, K, V> КэшированноеЗамыкание<F, K, V>
where
F: Fn(K) -> V,
K: Clone + std::hash::Hash + Eq,
V: Clone
{
function new(функция: F) -> Self {
КэшированноеЗамыкание {
функция,
кэш: HashMap::new()
}
}
function выполнить(&mut self, ключ: K) -> V {
match self.кэш.get(&ключ) { case Some(значение) => return значение.clone(), case None => {} }
let результат = (self.функция)(ключ.clone())
self.кэш.insert(ключ, результат.clone())
результат
}
function размерКэша(&self) -> usize {
self.кэш.len()
}
function очиститьКэш(&mut self) {
self.кэш.clear()
}
}
// Пример использования
let общийВектор = Arc::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
let замыканиеДоступа = создатьПотокобезопасноеЗамыкание(общийВектор)
print!("Доступ к значению: {:?}", замыканиеДоступа(3))
let mut кэшФибоначчи = КэшированноеЗамыкание::new(|н: i32| -> i64 {
if н <= 1 { н as i64 } else {
// В реальности это должно использовать кэш рекурсивно,
// но здесь упрощённая версия
(н as i64).pow(2) // Простое вычисление для примера
}
})
бенчмарк("Вычисление Фибоначчи", |_| {
for и in 1..=20 {
кэшФибоначчи.выполнить(и)
}
}, ())
print!("Размер кэша: {}", кэшФибоначчи.размерКэша())
🎯 Мастерство замыканий
Замыкания — одна из самых мощных возможностей Топаз:
✅ Когда использовать замыкания:
- Колбэк-функции и обработка событий
- Реализация функций высшего порядка
- Когда нужна инкапсуляция состояния
- Ленивое выполнение и частичное применение
⚠️ Меры предосторожности:
- Предотвращение утечек памяти (избегание циклических ссылок)
- Соображения производительности (избегание ненужного создания замыканий)
- Управление временем жизни (выбор подходящих методов захвата)
- Осознание сложности отладки
🚀 Преимущества замыканий Топаз:
- Гарантированная типобезопасность
- Автоматическое управление памятью
- Поддержка различных методов захвата
- Идеальная поддержка парадигмы функционального программирования
Пишите более выразительный код с замыканиями! ✨🚀