I'm not 100% sure the below is correct, but here's my guess.
The central difference between your two code samples is that the closure is formed in different isolation domains.
In your first example, the compiler needs to send the closure { 1 }
across an isolation boundary (from the main actor to non-isolated — the closure is main-actor-isolated because it inherits the isolation from the context where it is declared). This is an error because the closure is not Sendable
. You can fix this by explicitly marking the closure @Sendable
, like so:
_ = await context.perform(schedule:.enqueued){ @Sendable in 1 }
In your second example, the closure is formed in a non-isolated context and therefore doesn't inherit the main actor isolation from its surrounding context.
Other notes:
-
Arguably, the compiler in the first example should be able to see that the closure doesn't access any actor-isolated state and therefore infer it as
@Sendable
. I don't know if the compiler could conceivably do this in the future or if there are fundamental arguments against it. -
Alternatively, the compiler in the first example should be able to see that the closure isn't used in the caller's context (via region-based isolation) and therefore allow sending a non-Sendable closure. I think(!) the compiler doesn't do this for functions because the closure could be called multiple times, but I don't really understand it.
-
I thought that any main-actor-isolated type is automatically
Sendable
(because actors are implicitlySendable
), but apparently this isn't true for closures? Not sure.