Yes, that was exactly the problem I referred to.
Even without Codable
, a class that is not made Sendable
and not entirely marked @MainActor
, but has some parts that are marked as such, is expressing: "I can be created in any isolation context, but then I have to stay in that context and cannot be send to another one. Oh, and there are also some parts of me which can only be accessed on the @MainActor
's isolation context."
But if the @MainActor
isolated "parts" are properties, how are they even instantiated on any other isolation context? That is a contradiction unless it's calculated properties that do not rely on any state that's not isolated in the same way (which then would also have to be initialized somehow), OR it's similarly non-isolation-dependent functions. I'd say the use-cases for such a class are limited. Making it work as defined would also require some way manually guarantee the isolation works, maybe via locks or the like.
However, the fun thing is that if you define such a class, the compiler will only ever warn you about that once you, somewhere in your code, "expose" that contradiction. If you only ever instantiate it in a function that is (directly or indirectly) bound to @MainActor
, it will not scream (I think). In other words: It is perfectly valid to define a class with that "contradiction" as long as you do not "trigger" it.
Now, as soon as you make it adopt to Codable
, you get the warning, as Codable
's methods, by not being isolated to @MainActor
, explicitly require you to access the parts that are isolated (in the initializer not the initializer, actually it's Encodable
's func encode(to encoder: any Encoder)
that requires access to the isolated parts...).
That is something earlier Swift simply did not care about, it was not expressible.
Btw, of course I am not saying that resolving this can be done by fixing the "contradiction" in the class's definition (by e.g. making it entirely @MainActor
). That would still clash with Codable
as the protocol is designed to be useful for types that run on any isolation context. @ConfusedVorlon is right, imo, that a more restrictive/"clever" protocol would be useful. What I am saying is that now that Swift has syntax and semantics to explicitly express these concurrency aspects (isolation, sendability, etc.), situations like this are unavoidable. You can mitigate some things with @preconcurrency
and the like, but here that won't do. As written the code basically says "Yes, I will be using this unsendable, partially isolated class from multiple isolation contexts, including the isolated parts". The entire point of the "new world" of data race safety is that the compiler complains.