Why is this not allowed in 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 was overly conservative regarding passing non-Sendable
types to different contexts. Specifically, we might create a non-Sendable
instance, and pass it to some other context, but not use it outside of that new context. Swift 6 (specifically SE-0414) has improved this. As WWDC 2024 video What’s new in Swift says:
To ensure safety, complete concurrency checking in Swift 5.10 banned passing all non-
Sendable
values across actor isolation boundaries. Swift 6 can recognize that it is safe to pass non-Sendable
values, in scenarios where they can no longer be referenced from their original isolation boundary.
So, as you noted, in Swift 6 (in Xcode 16 beta 3), you will get a warning with the following code:
In this case, though, it is the presence of the reference to nonSendable
in the for
-in
loop that affects its isolation region. E.g., remove that reference and the error goes away:
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) { }
}
This Swift 6 behavior is an improvement over Swift 5.10. See SE-0414 – Region based Isolation for a lengthy discussion regarding what improvements Swift 6 provides and the limitations that are still imposed.
For the sake of clarity, your original example (with both the for
-in
loop and the forEach
closure) did not actually manifest a data race. But the question is whether the compiler can guarantee that the code is free from races: At this point it cannot.
In terms of work-arounds, either avoid attempting to use the nonSendable
instance from two different regions, or make the object Sendable
.