Почему это не разрешено в 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) { }
}





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