Skip to content

Commit 30ed41b

Browse files
authored
Merge pull request #132 from hborla/isolation-non-sendable-closure
Explain the default isolation behavior of closures.
2 parents 263f9fc + 2e7fa65 commit 30ed41b

File tree

1 file changed

+37
-5
lines changed

1 file changed

+37
-5
lines changed

Guide.docc/DataRaceSafety.md

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -350,10 +350,38 @@ But these are all examples of _declarations_.
350350
It is also possible to achieve a similar effect with function values, through
351351
isolation _inheritance_.
352352

353-
A closure can capture the isolation at its declaration site, instead of the
354-
isolation being statically defined by its type.
355-
This mechanism may sound complex, but in practice it allows very natural
356-
behaviors.
353+
By default, closures are isolated to the same context they're formed in.
354+
For example:
355+
356+
```swift
357+
@MainActor
358+
class Model { ... }
359+
360+
@MainActor
361+
class C {
362+
var models: [Model] = []
363+
364+
func mapModels<Value>(
365+
_ keyPath: KeyPath<Model, Value>
366+
) -> some Collection<Value> {
367+
models.lazy.map { $0[keyPath: keyPath] }
368+
}
369+
}
370+
```
371+
372+
In the above code, the closure to `LazySequence.map` has type
373+
`@escaping (Base.Element) -> U`. This closure must stay on the main
374+
actor where it was originally formed. This allows the closure to capture
375+
state or call isolated methods from the surrounding context.
376+
377+
Closures that can run concurrently with the original context are marked
378+
explicitly through `@Sendable` and `sending` annotations described in later
379+
sections.
380+
381+
For `async` closures that may be evaluated concurrently, the closure can still
382+
capture the isolation of the original context. This mechanism is used by the
383+
`Task` initializer so that the given operation is isolated to the original
384+
context by default, while still allowing explicit isolation to be specified:
357385

358386
```swift
359387
@MainActor
@@ -364,13 +392,17 @@ func eat(food: Pineapple) {
364392
// allowing the closure's body to inherit MainActor-isolation
365393
Chicken.prizedHen.eat(food: food)
366394
}
395+
396+
Task { @MyGlobalActor in
397+
// this task is isolated to `MyGlobalActor`
398+
}
367399
}
368400
```
369401

370402
The closure's type here is defined by `Task.init`.
371403
Despite that declaration not being isolated to any actor,
372404
this newly-created task will _inherit_ the `MainActor` isolation of its
373-
enclosing scope.
405+
enclosing scope unless an explicit global actor is written.
374406
Function types offer a number of mechanisms for controlling their
375407
isolation behavior, but by default they behave identically to other types.
376408

0 commit comments

Comments
 (0)