В Perl поддерживаются анонимные подпрограммы и замыкания. Анонимные подпрограммы объявляются через sub без имени и возвращают ссылку на код. Замыкание — это подпрограмма, которая "захватывает" лексическую область видимости, включая переменные, существовавшие при ее создании.
Пример анонимной подпрограммы и closure:
sub make_incrementer { my $inc = shift; return sub { $_[0] + $inc }; } my $inc10 = make_incrementer(10); print $inc10->(5); # Выведет 15
Closures активно применяются для создания фабрик функций, генераторов и callback-ов (например, в map/grep, обработчиках событий).
Важный момент: если closure ссылается на переменные, которые, в свою очередь, указывают на саму closure (прямо или через структуру), возникает циклическая ссылка и возможна утечка памяти.
Переменные, замкнутые в closure, когда освобождаются? Есть ли разница в поведении для my и our?
Ответ: Переменные my, замкнутые внутри closure, остаются живыми до тех пор, пока существует хотя бы одна ссылка на саму closure. Они не освобождаются по завершению функции, их время жизни — всё время жизни closure. Для our переменных такого поведения нет — они доступны для всех closure и освобождаются по завершении программы. Забыв удалить closure, можно получить утечки памяти.
Пример проблемы:
my $cref; { my $val = 5; $cref = sub { $val++ }; } # $val по-прежнему существует, пока жива $cref
История
В серверном приложении к каждому пользователю создавалось замыкание-контекст для callback. Но closure также ссылалось на user-структуру, что приводило к циклической ссылке. Из-за этого garbage collector не очищал объекты пользователя даже после logout — утечки памяти росли экспоненциально.
История
В приложении-демоне для фоновой обработки событий использовались closures с замкнутыми переменными-счетчиками, но забыли обнулять массивы, на которые они ссылались. Итог — из-за пары забытых closures случайно копились старые данные сообщений, до отказа демона пришлось проводить ручную чистку кучи.
История
Разработчик попытался использовать our-переменную в closure, ожидая "замкнутое" поведение — но все closures делили одну переменную, что приводило к гонкам данных при параллельном исполнении. Баг проявился при одновременной работе пользователей с разными параметрами (ошибочные вычисления).