Consider this simple example, where we have an Actor and a Class which holds a reference to that Actor:
actor MyActor
{
func doWork() -> Int {
return 42
}
}
class MyClass
{
let worker: MyActor = MyActor()
func swift6IsJustGreat()
{
Task {
let number: Int = await worker.doWork() // ERROR: Capture of 'self' with non-sendable type 'MyClass' in a `@Sendable` closure
print(number)
}
}
}
Xcode 16 Beta 1, when using Swift 6 Language Mode, shows this error on the line where we attempt to use the actor:
Capture of 'self' with non-sendable type 'MyClass' in a `@Sendable` closure
I understand the error. worker
is a property of MyClass
and the Task closure is capturing the instance of MyClass
, which is not Sendable
. Got it. But then...how the hell do I use anything in concurrency unless it's a global?
If my actor is an iVar on a non-Sendable class, how can I ever call anything on the actor?
Capture worker
into the Task rather than self
:
Task { [worker] in // This makes a local copy of `self.worker`
let number: Int = await worker.doWork() // No problem
print(number)
}
The Task does not actually need self
. It doesn't do anything with it, and since worker
is a let
property, it is impossible for it to change between the time Task
begins and when worker
is used. (If it were var
, then it would be even more important that it be captured rather than self
, since it could change in a thread-unsafe way.)
An Actor is always Sendable, so can always be captured by a Task.