Каким образом параметр isolated меняет изоляцию функции, принимающей actor?
Параметр isolated временно передаёт функции изоляцию конкретного экземпляра actor. Поэтому тело функции выполняется на исполнительном контексте этого actor и может обращаться к его изолированному состоянию синхронно внутри самой функции.
Вызов такой функции из внешнего контекста обычно требует перехода к actor и поэтому обозначается через await, даже если сама функция не объявлена как async.
Actors появились в Swift Concurrency как средство безопасной защиты изменяемого состояния: доступ к состоянию actor должен проходить через его изоляцию, а конкурентный код не должен напрямую разделять это состояние.
Обычный интерфейс actor нередко приводит к нескольким последовательным изолированным вызовам. Параметр isolated позволяет выразить, что целая вспомогательная функция работает в изоляции переданного actor, уменьшая число переходов и сохраняя проверяемую компилятором безопасность.
Предположим, операция должна изменить несколько свойств одного actor как единую последовательность. Если реализовать её через отдельные вызовы методов, между ними могут происходить приостановки и выполняться другие обращения к actor.
Если же вынести логику во внешнюю функцию без isolated, она не получит синхронный доступ к состоянию actor. Попытка обойти это ограничение через небезопасный доступ или ручные блокировки нарушает модель Swift Concurrency и может привести к гонкам данных.
isolated у параметра означает: функция изолирована не глобально и не относительно любого actor, а относительно конкретного actor, переданного в этот параметр. Внутри функции его изолированные свойства и методы доступны без дополнительных await между операциями.
В apply обе операции выполняются в изоляции одного Ledger. Вызов из update может потребовать await, поскольку вызывающая задача должна перейти к исполнительному контексту этого actor; после входа тело apply не обязано приостанавливаться между двумя изменениями.
Это не превращает произвольный объект в actor и не делает его состояние глобально доступным. Изоляция действует только в пределах вызова функции, а переданный actor остаётся владельцем своего состояния. Функция не должна одновременно получать несколько независимых isolated-параметров: такая модель создавала бы неоднозначность, на каком actor выполнять тело.
Преимущество подхода — возможность сгруппировать связанную операцию и сократить количество переходов к actor. Компромисс состоит в том, что функция становится тесно связана с actor-изоляцией: её нельзя свободно вызвать с обычным значением или использовать как универсальную синхронную функцию.
В модуле платежей нужно списать сумму и одновременно записать служебное изменение баланса. Первый вариант — открыть два отдельных метода actor и вызвать их последовательно. Он прост для чтения, но между вызовами могут появиться точки ожидания, а инвариант операции будет сложнее выразить как единое действие.
Второй вариант — передать actor во внешнюю функцию без isolated. Такой код не получает легального синхронного доступа к состоянию; добавление await к каждому обращению увеличивает число переходов и не делает составную операцию автоматически атомарной.
Выбран вариант с isolated-параметром для одной короткой операции, поддерживающей инвариант баланса. Это даёт единый изолированный участок и сохраняет статическую проверку. Для длинной операции с потенциально блокирующим вводом-выводом изоляцию не удерживают искусственно: внешние данные сначала подготавливают отдельно, а краткое изменение состояния выполняют в изолированной функции.
Дополнительный вопрос 1. Может ли функция с isolated-параметром содержать await?
Да, если она объявлена как async или вызывает асинхронные операции. Но isolated не делает всю последовательность после await неделимой: actor может обслужить другие сообщения во время приостановки. Поэтому изоляция защищает отдельные обращения к состоянию, но не гарантирует атомарность составной операции через точку await.
Дополнительный вопрос 2. Чем isolated-параметр отличается от обычного вызова метода actor?
Обычный метод actor уже изолирован относительно своего self. isolated позволяет применить ту же изоляцию к свободной или другой вспомогательной функции, получившей actor как параметр. Это полезно для группировки логики и уменьшения лишних переходов, но не отменяет правил доступа и не переносит состояние actor наружу.
Дополнительный вопрос 3. Безопасно ли сохранить isolated-параметр для использования после выхода из функции?
Нет, isolated описывает режим выполнения текущего вызова, а не право извлечь или передать наружу защищённое состояние. Ссылку на сам actor можно передать дальше согласно правилам Swift Concurrency, но доступ к его изолированным данным по-прежнему должен происходить через actor-изоляцию. Попытка сохранить отдельное внутреннее значение или ссылку на изменяемый объект и использовать её вне контроля actor может снова создать гонку данных.