Замыкания в актерах: отправка «nonSendable» рискует вызвать гонку данных

Почему это не разрешено в Swift 6 (Xcode 16 Beta 3)?

class NonSendable { }

actor MyActor {
    func foo() {
        let nonSendable = NonSendable()

        for _ in 1...3 {
            // ✅ Compiles fine
            bar(nonSendable)
        }
        
        (1...3).forEach { _ in
            // ❌ Sending 'nonSendable' risks causing data races
            // 'self'-isolated 'nonSendable' is captured by a actor-isolated
            // closure. actor-isolated uses in closure may race against later
            // nonisolated uses
            bar(nonSendable)
        }
    }
    
    func bar(_: NonSendable) { }
}

Действительно очень интересный случай. 🤔 Если добавить nonisolated к bar(_:), он перестанет жаловаться. Так может быть, изоляция актера bar(_:) каким-то образом предотвратит его немедленное выполнение (или, скорее, его гарантию) и, возможно, запланирует его выполнение на self позже? Здесь просто дико предполагаю. Мне бы тоже было интересно узнать точную причину.

fabianm 18.07.2024 16:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
200
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Swift 5.10 был слишком консервативен в отношении передачи типов, отличных от Sendable, в разные контексты. В частности, мы можем создать экземпляр, отличный от Sendable, и передать его в какой-то другой контекст, но не использовать его вне этого нового контекста. В Swift 6 (в частности SE-0414 ) это улучшилось. В видео WWDC 2024 Что нового в Swift говорится:

В целях обеспечения безопасности полная проверка параллелизма в Swift 5.10 запретила передачу всех значений, отличных от Sendable, через границы изоляции актеров. Swift 6 может распознавать, что передавать значения, отличные от Sendable, безопасно в сценариях, где на них больше нельзя ссылаться из исходной границы изоляции.

Итак, как вы заметили, в Swift 6 (в Xcode 16 beta 3) вы получите предупреждение со следующим кодом:

Однако в данном случае именно наличие ссылки на nonSendable в цикле for-in влияет на область изоляции. Например, удалите эту ссылку, и ошибка исчезнет:

actor MyActor {
    func foo() {
        let nonSendable = NonSendable()

        // for _ in 1...3 {
        //     bar(nonSendable)
        // }

        (1...3).forEach { _ in
            // ✅ Compiles fine
            bar(nonSendable)
        }
    }

    func bar(_ object: NonSendable) { }
}

Такое поведение Swift 6 является улучшением по сравнению с Swift 5.10. См. SE-0414 – Изоляция на основе регионов, где подробно обсуждаются улучшения, которые предоставляет Swift 6, и ограничения, которые все еще налагаются.


Для ясности: ваш исходный пример (как с циклом for-in, так и с замыканием forEach) на самом деле не демонстрировал гонку данных. Но вопрос в том, может ли компилятор гарантировать отсутствие гонок в коде: на данный момент он не может этого сделать.

Что касается обходных путей: либо избегайте попыток использовать экземпляр nonSendable из двух разных регионов, либо создайте объект Sendable.

Предполагалось ослабить слишком строгие правила, но в данном случае происходит обратное. Когда я меняю языковую версию Swift на 5, она компилируется нормально.

John 21.07.2024 22:58

Другие вопросы по теме