Освобождение ресурсов
Процесс освобождения ресурсов является распространённой задачей: закрыть файловый дескриптор, закрыть соединение и т.д..
При ручном освобождении ресурсов (она же функция close
) могут возникнуть множество проблем — это можно забыть сделать или закрыть слишком рано.
Пример ручного закрытия файла
let read_first_line_from_file filename =
let file = open_in filename in
let line = input_line file in
close_in file;
line
Как это в других языках?
В языке вроде C++ или Rust для это существует нативная поддержка механизма RAII, но он не сильно подходит средам с автоматическим управлением памятью (т.е GC).
Далее приведены идиомы и возможные варианты как это делать правильно на OCaml.
Функция открытия — также функция закрытия
Смотрите также
Пример
Абстрактный пример того, как реализуется идиоматичная функции автоматического освобождения некоторого ресурса.
let initialize _ = (* ... *)
let dispose _ = (* ... *)
let with_initialize _ f =
let resource = initialize _ in
Fun.protect
(fun () -> f resource)
~finally:(fun () -> dispose resource)
Данный шаблон вы можете встретить как в стандартной библиотеке, так и в множестве других, ибо он позволяет правильно завершить код даже в случае исключения внутри callback-функции.
Освобождение при уничтожении объекта
Используя интерфейс сборщика мусора (модуль Gc) мы можем установить функцию, что должна быть выполнена во время освобождения объекта сборщика мусора.
Работает исключительно на heap-allocated объектах!
Стоит понимать
Освобождения ресурса произойдёт только тогда, когда OCaml решит очистить объект, а это может не произойти вообще. Смотрите Memory management.
Библиотека Lwt
В пакете lwt.unix
(часть Lwt) есть модуль Lwt_gc
и функция finalise_or_exit
, которая гарантирует, что при завершении программы ресурс будет освобождён (будет вызвана функция, в которой произойдёт освобождение).
Пример
let main =
let res = String.make 10 'x' in
Lwt_gc.finalise_or_exit (Lwt_io.printlf "free '%s'") res;
Lwt.return_unit
Switches
Switch (свитч) — область видимости, к которой привязываются ресурсы, и по завершению которой ресурсы должны будут быть освобождены.
Эдакая прокаченная версия with-функций и finalise из Gc.
Этот паттерн можно особенно ярко встретить в библиотеке Eio. Он там повсеместен и без него нельзя ничего сделать, так как ресурс должен быть к чему-то привязан.
Пример использования Eio.Switch
let run_client ~net ~addr =
Switch.run ~name:"client" @@ fun sw ->
traceln "Client: connecting to server";
let flow = Eio.Net.connect ~sw net addr in
(* Read all data until end-of-stream (shutdown): *)
traceln "Client: received %S" (Eio.Flow.read_all flow)
Пример использования Lwt_switch
В Lwt тоже можно найти свитчи как доп. абстракции — модуль Lwt_switch.
let main =
Lwt_switch.with_switch @@ fun sw ->
let* file = Lwt_io.open_file "some-file" ~mode:Output in
Lwt_switch.add_hook (Some sw) (fun () -> Lwt_io.close file);
Lwt.return_unit
Читайте подробнее на основной странице.