В библиотечном алгоритме замыкание может бросать ошибку: какую гарантию даёт rethrows функции-обёртке?
rethrows гарантирует, что функция-обёртка может завершиться с ошибкой только если ошибку выбросило переданное ей замыкание или другой throwing-параметр. Если передано невыбрасывающее замыкание, вызов такой функции не обязан использовать try. Самостоятельно выбрасывать новую ошибку, не полученную от параметра, функция с rethrows не может.
Функции высшего порядка часто лишь организуют вызов пользовательского замыкания: перебирают элементы, преобразуют данные или выполняют операцию в заданном порядке. Требовать try от каждого вызова такой обёртки было бы избыточно, если конкретное переданное замыкание не умеет выбрасывать ошибки.
rethrows выражает зависимость поведения функции от её аргумента. Это позволяет сохранить проверку ошибок на этапе компиляции, но не заставлять вызывающий код обрабатывать ошибку там, где фактическая операция не может её выбросить.
Обычный throws означает, что функция потенциально может завершиться ошибкой независимо от переданных аргументов. Поэтому вызывающий код должен учитывать ошибку всегда, даже если переданное замыкание в конкретном вызове невыбрасывающее.
Если обёртка помечена rethrows, но внутри пытается самостоятельно выбросить ошибку валидации или преобразовать не связанную с замыканием ошибку, это нарушает контракт и не компилируется. Для такой логики нужен обычный throws либо другой дизайн API.
rethrows применяется к функции, у которой есть параметр-функция с throws. Обёртка может вызвать этот параметр через try и передать ошибку наружу, но не получает право создавать произвольную ошибку независимо от него.
В первом вызове замыкание не выбрасывает ошибку, поэтому try не требуется. Во втором замыкание может завершиться ошибкой, и она распространяется через run; сама функция run её не перехватывает и не изменяет.
Если функция принимает несколько throwing-замыканий, ошибка может возникнуть при вызове любого из них. При этом rethrows не является механизмом обработки ошибок: он только ограничивает источник возможной ошибки и позволяет компилятору вывести необходимость try из фактических аргументов.
Допустим, библиотека предоставляет универсальный алгоритм выполнения операции над ресурсом. Вариант с throws проще написать, но каждый вызов потребует try, даже когда операция гарантированно безопасна. Вариант без обработки ошибок не подходит, потому что пользовательская операция может завершиться неудачно.
Выбор rethrows сохраняет оба свойства: безопасная операция вызывается без лишнего try, а throwing-операция требует явной обработки ошибки. Если же библиотека должна дополнительно проверять состояние ресурса и выбрасывать собственную ошибку, следует выбрать throws, поскольку источник ошибок уже не ограничивается переданным замыканием.
rethrows выбросить собственную ошибку в отдельной ветке?Нет, если эта ошибка не является результатом вызова throwing-параметра. Например, самостоятельная проверка входных данных с последующим throw не соответствует контракту rethrows. В такой ситуации функцию нужно объявить как throws, даже если она также вызывает переданное замыкание.
rethrows отличается от обычного throws для вызывающего кода?throws заставляет учитывать возможность ошибки при каждом вызове функции. rethrows позволяет компилятору определить это по конкретным замыканиям: вызов с невыбрасывающим замыканием может быть выполнен без try, а вызов с выбрасывающим требует try. При этом внутри реализации rethrows всё равно нужно использовать try при вызове throwing-параметра.
rethrows скрыть ошибку, если замыкание само её перехватило?Да, замыкание может обработать собственную ошибку внутри себя и вернуть обычный результат. В этом случае для внешней функции фактически не произошло выбрасывания, поэтому вызов обёртки не требует try. Если же замыкание перехватывает ошибку и затем повторно выбрасывает её, она по-прежнему распространяется через rethrows.