When is a type Sendable, and what does the compiler check when you declare it?
Predict first
Do these Sendable declarations compile?
struct Point: Sendable { var x: Int; var y: Int }
final class Box: Sendable { let v: Int; init(_ v: Int) { self.v = v } }
enum Kind: Sendable { case a, b(Point) }
let p = Point(x: 1, y: 2)
Task { if case .b(let q) = Kind.b(p) { print(p.x, Box(3).v, q.y) } }
await Task.yield()
- Yes and it prints "1 3 2"
- No: classes cannot be Sendable
- No: enums need @unchecked
Predict first
And a Sendable struct holding a mutable class?
final class Counter { var n = 0 }
struct Bad: Sendable { var c: Counter }
- Compile error
- It compiles
Mental model. A document is safe to photocopy and hand to everyone if it cannot change. A shared whiteboard needs a rule about who writes on it.
Sendable means a value can be safely used from several concurrency domains at once. The rules are mostly mechanical. Value types (structs and enums) are Sendable when all their stored properties and payloads are; for non-public ones the compiler even infers it. A class is Sendable only if it is final, has only immutable (let) stored properties of Sendable types, and has no superclass with state (so Box in the check is fine). Actors are always Sendable, and closures that cross domains are @Sendable: they may only capture Sendable values.
When you declare the conformance, the compiler verifies it: struct Bad: Sendable { var c: Counter } is rejected because the stored property has a non-Sendable class type (“stored property of Sendable-conforming struct has non-Sendable type”). Public types must declare conformance explicitly (no inference across module boundaries, which would make the promise accidental), and removing a conformance later is a source-breaking API change. Fix a rejection by making the property immutable and Sendable, by making the field a Sendable type (an actor, a struct), by protecting the class internally (see the escape hatches), or by avoiding the crossing altogether with region-based isolation and sending (Q297).
Value types with Sendable members, a final class with immutable fields, and an enum with a Sendable payload are all valid.
A stored property of non-Sendable type breaks the conformance.
Common traps
- Assuming a mutable final class is Sendable because it is “only used from one place”. The compiler cannot know, and rejects it.
- Adding
Sendableto a public type casually. It is a promise your clients rely on, and cannot be silently removed.
They might follow up with
- Are Swift’s collections Sendable? Yes, when their elements are; an
Array<Counter>of a non-Sendable class is not. - What about
@Sendableclosures that capturevar? Mutable captures are rejected unless the variable is isolated to an actor or protected.
Related questions
- 10.10.2 How do you choose between an actor, a Mutex, an unfair lock, an atomic and a serial queue to protect shared…
- 10.10.3 What are @unchecked Sendable and nonisolated(unsafe) promising, and what does the compiler still check when…
- 10.5.1 How does region-based isolation work, and what does the sending keyword add on top of Sendable?
- 10.5.2 How do you migrate an existing app to Swift 6 and strict concurrency without breaking it?
How do you choose between an actor, a Mutex, an unfair lock, an… →