final class NonSendable {
let mainActorIsolated: MainActorIsolated
init(mainActorIsolated: MainActorIsolated) {
self.mainActorIsolated = mainActorIsolated
Task { @MainActor in
use(mainActorIsolated) // <-- Error
}
}
@MainActor
func use(_ mainActorIsolated: MainActorIsolated) {
print(mainActorIsolated.value)
}
}
@MainActor
final class MainActorIsolated {
var value: Int = 7
}
The emitted error reads:
Sending 'mainActorIsolated' risks causing data races
with additional info:
Task-isolated 'mainActorIsolated' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses
This seems to be related to the fact that the class is not Sendable, but I don't understand the logic here. I haven't done a deep dive into region based isolation yet, so I might be misinterpreting, but it seems like the non-sendability of the instance being initialized is for some reason triggering the arguments to be treated as if they too have to limited to a single region, despite being Sendable. Can anyone help me understand this?
Perhaps another way to ask the question is, what does it mean for a @MainActor-isolated instance to be "task-isolated"? I would have thought that this is meaningless.
Try explicitly marking MainActorIsolated as Sendable. I've noticed some bugs where marking something with a global actor doesn't infer Sendable properly, so it has to be explicit sometimes.
if you run this example on the latest nightly build (godbolt), then the error messaging is:
<source>:7:13: error: sending 'self' risks causing data races
5 | self.mainActorIsolated = mainActorIsolated
6 | Task { @MainActor in
7 | use(mainActorIsolated) // <-- Error
| |- error: sending 'self' risks causing data races
| `- note: task-isolated 'self' is captured by a main actor-isolated closure. main actor-isolated uses in closure may race against later nonisolated uses
8 | }
9 | }
which i think makes sense in this case. self is non-sendable and init is not isolated, so the isolation checking conservatively warns about the potential race in different isolations. i believe the confusion here may stem from a possible off-by-one error in the warning you're seeing which caused the diagnostics to surface the wrong problematic reference (which was recently fixed here).
that being said, it's likely that this pattern of having multiple isolations within a non-sendable type will prove to be generally unwieldy. in this article on problematic swift concurrency patterns, @mattie dubs this 'split isolation'.
however, if you're just looking for a fix to address the specific error, in this instance, you can resolve it by doing any of the following:
making the use(...) method static
making the init@MainActor (which perhaps implies the type should be annotated instead...)
making the NonSendable type @MainActor (naming confusion aside)