SendableMetatype
A type whose metatype can be shared across arbitrary concurrent contexts without introducing a risk of data races.
Declaration
protocol SendableMetatype : ~Copyable, ~EscapableOverview
When a generic type T conforms to SendableMetatype, its metatype T.Type conforms to Sendable. All concrete types implicitly conform to the SendableMetatype protocol, so its primary purpose is in generic code to prohibit the use of isolated conformances along with the generic type.
A generic type T will need a SendableMetatype conformance when its metatype is shared across concurrency boundaries. For example,
protocol P {
static func f()
}
func useFromAnotherTask<T: P>(_: T.Type) {
Task { @concurrent in
T.f() // error: capturing non-Sendable type `T.Type` in a concurrently-executing task
}
}The potential data race above would occur when useFromAnotherTask is provided with an isolated conformance to P. For example:
@MainActor
class MyModel: @MainActor P {
/* implicitly @MainActor */
static func f() {
/* on the main actor */
}
}
useFromAnotherTask(MyModel.self)Here, the error within the body of useFromAnotherTask is preventing the isolated conformance from leaving the current task and actor. The signature of useFromAnotherTask can be adjusted to introduce a requirement on SendableMetatype:
func useFromAnotherTask<T: P & SendableMetatype>(_: T.Type) {
Task { @concurrent in
T.f() // error: okay, T.Type is Sendable
}
}
useFromAnotherTask(MyModel.self) // error: cannot use main-actor-isolated conformance `MyModel: P` for a `SendableMetatype`-conforming type parameter `T`The Sendable protocol inherits from SendableMetatype, so any generic type T with a requirement T: Sendable will have the implied requirement T: SendableMetatype.