Sendable is a contract, and switching from Sendable to not Sendable is a breaking change. So for vendors of public types, the benefit (as originally envisioned) is that you will not accidentally promise something as Sendable just because it happens to be Sendableat the moment based on the recursive rules.
yeah, that’s what i thought too while i was using the limited concurrency checking mode. but real world experience with the strict concurrency mode is starting to convince me that this is just not a sensible approach. the number of types that need this is just too high, and the only reason i wasn’t noticing this problem before was because the limited concurrency checking mode was letting these types get away with not having explicit Sendable conformances.
much of what you say about Sendable is just as valid for Copyable. but we are not required to spray Copyable everywhere, because there is recognition that adding this conformance to every public type in a project is counterproductive.
But the compiler doesn’t infer Copyable conformances for types, it just makes everything Copyable by default with an opt-out.
I believe we already infer Sendable from field types for non-public types, and it causes request cycles during type checking, so I’m generally not a fan of this sort of thing from an implementation perspective.
so that i don’t mindlessly attach a Sendable to it later. and this is not a particularly common situation either. and mindlessly attaching Sendable to things that should not have it becomes a real problem when you are constantly attaching Sendable to things to resolve compiler warnings.
I think RBI will mostly make it so that app developers (or more generally leaf code) won't need to mark things as Sendable very often because genuinely shared values should be pretty uncommon compared to values which move between isolation domains, but I don't think it changes anything for library code.
RBI will increase the frequency with which users will have to open bug reports asking for a type to be made Sendable because it made the library author not notice that they forgot. I don't consider this a meaningful problem with RBI, but as a library author things which make me notice problems later rather than sooner certainly aren't a benefit. RBI won't actually eliminate the need to mark anything as Sendable which could be because inevitably users will want to use every type in a sendable context (even if it makes no sense to do so).
“Almost everywhere” is a really strong statement for a language with first-class classes, Any, and UnsafePointer. It’s certainly possible to make a different trade-off here (Rust did, though it was over a decade ago when it made that decision!), but it’d be better to do it without hyperbole.
UnsafePointer is a textbook example of an unsafe construct that gets wrapped by some “safe” abstraction which is ostensibly Sendable, but only because the lax concurrency mode doesn’t require the wrapper type to only contain Sendable stored properties.
under the strict concurrency mode, it raises warnings because the wrapped pointer is not Sendable (SE-0331).
but such a type would usually still carry a
@available(*, unavailable, message: "not Sendable because ...")
to document why it is not Sendable, and to force the warning to appear even with the lax concurrency mode.
this isn’t a very common case, it’s just an example of a place where being able to write PointerWrapper:~Sendable would be helpful.
Any is already a problem when using the strict concurrency mode, it inevitably needs to be refactored into any Sendable in code that uses concurrency. Any without any Sendable qualifications just isn’t that useful in a concurrent context.
this is a weird inverse of the problem we have with ~Copyable and Any, and you could imagine an alternate reality where Any didn’t imply Copyable and we had a similar problem with underconstrained existentials that cannot be copied.
i personally think classes with mutable state are going to become an endangered species as the strict concurrency mode becomes more popular, they just don’t compose very well with concurrency.
for a concrete case study, you can browse through some of the discussions about SwiftNIO’s ClientBootstrap class, which was conceived before the language had first-class concurrency support, and is now widely viewed as ill-adapted for modern concurrency features.
i personally think a lot (not all, but a lot) of these types will have to evolve into actors if they need shared state, and structs/enums otherwise.
i don’t think these exceptions really contradict my argument that Sendable is too fundamental a conformance to have to be sprayed everywhere preemptively, or reported as an issue when it is missing. the types that are ~Sendable are the special cases, and i think as the warnings pile up they are going to become less common and the ones that remain will become more-clearly documented as such.
RBI will hopefully make mutable classes usable, but in Swift 5.9 I do increasingly think that if you're using concurrency you have Sendable types, @MainActor types, and actors. Anything else is just kind of unusable. Strictly isolation-context isolated types just don't work very well with lots of suspension points; with non-threadsafe types you want much more corse-grained interactions.