I'm currently working on a Swift Concurrency Course and I try to understand why public non-frozen enums aren't implicitly Sendable
.
Reading SE-302 and quoting:
Public non-frozen structs and enums do not get an implicit conformance, because doing so would present a problem for API resilience: the implicit conformance to
Sendable
would become part of the contract with clients of the API, even if it was not intended to be. Moreover, this contract could easily be broken by extending the struct or enum with storage that does not conform toSendable
.
I realize I find it hard to wrap my head around "the contract with clients" as in my mind, clients of Swift packages are developers and they can simply update to the latest version of the framework.
That aside: key point for me here is that nobody can add cases to the enum unless they have access to the source code. Yet, after further investigation, I wrote the following title and paragraph for my lesson:
Why public non-frozen enums aren’t implicitly Sendable (even though no one else can add cases)
Even though no one outside your module can add new cases to your public enum, Swift still does not automatically make it
Sendable
if it isn’t marked as@frozen
. This is because, as the library author, you might decide to add new cases or change associated values in future versions of your library. Swift’s library evolution model lets clients compile against version 1 of your library and then, at runtime, link against a newer version without recompiling. If you add a new case later, client code compiled against the original version could encounter a case it doesn’t recognize, which breaks safety guarantees.Marking an enum as
@frozen
explicitly informs the compiler and your clients that no new cases will ever be added. This enables Swift to fully understand the type’s structure and safely infer Sendable if all cases and associated values are themselvesSendable
. Without@frozen
, Swift cannot make this guarantee and thus requires you to declareSendable
conformance explicitly. This approach guarantees that concurrency safety is maintained even as libraries evolve.
Now, I feel like my answer is close to being right, but it also reminds me of library evolution being opt-in (read more about it here). With that in mind:
- Shouldn't we be able to allow public enums to be implicitly
Sendable
for non-library evolution supporting packages? - Do you think my two paragraphs properly explain the current situation?