Add rule to avoid using @unchecked Sendable
#237
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
This PR proposes a new rule to prefer avoiding usage of
@unchecked Sendable
. Instead, we should use a standardSendable
conformance instead where possible. If working with a type from a module that has not yet been updated to support Swift Concurrency, suppress concurrency-related errors using@preconcurrency import
.Reasoning
@unchecked Sendable
provides no guarantees about the thread safety of a type, and instead unsafely suppresses compiler errors related to concurrency checking.There are typically other, safer methods for suppressing concurrency-related errors:
1. Use
Sendable
instead of@unchecked Sendable
, with@MainActor
if appropriateA
Sendable
conformance is the preferred way to declare that a type is thread-safe. The compiler will emit an error if a type conforming toSendable
is not thread-safe. For example, simple value types and immutable classes can always safely conform toSendable
, but mutable classes cannot:Mutable classes can be made
Sendable
and thread-safe if they are isolated to a single actor / thread / concurrency domain. Any mutable class can be madeSendable
by isolating it to a global actor using an annotation like@MainActor
(which isolates it to the main actor):2. Use
@preconcurrency import
If working with a non-
Sendable
type from a module that hasn't yet adopted Swift concurrency, suppress concurrency-related errors using@preconcurrency import
.3. Restructure code so the compiler can verify that it is thread-safe
If possible, restructure code so that the compiler can verify that it is thread safe. This lets you use a
Sendable
conformance instead of an unsafe@unchecked Sendable
conformance.When conforming to
Sendable
, the compiler will emit an error in the future if you attempt to make a change that is not thread-safe. This guaruntee is lost when using@unchecked Sendable
, which makes it easier to accidentially introduce changes which are not thread-safe.For example, given this set of classes:
the compiler can't verify
PlanetaryBody
isSendable
because it is notfinal
. Instead of using@unchecked Sendable
, you could restructure the code to not use subclassing:Using
@unchecked Sendable
when necessarySometimes it is truly necessary to use
@unchecked Sendable
. In these cases, you can add a// swiftlint:disable:next no_unchecked_sendable
annotation with an explanation for how we know the type is thread-safe, and why we have to use@unchecked Sendable
instead ofSendable
.A canonical, safe use case of
@unchecked Sendable
is a class where the mutable state is protected by some other thread-safe mechanism like a lock. This type is thread-safe, but the compiler cannot verify this.It is also reasonable to use
@unchecked Sendable
for types are thread-safe in existing usage but can't be refactored to support a properSendable
conformance (e.g. due to backwards compatibility constraints):Please react with 👍/👎 if you agree or disagree with this proposal.