When Swift 5.7 drops, what will be the difference between AnyHashable and any Hashable? Or for that matter, AnyView and any View? Or AnyPublisher vs. any Publisher?

I'm writing a blog about how to use protocols in Swift and just looking to hopefully explain what's sure to be a confusing point going forwards. What would be the pros/cons of using these new existentials vs. the type-erasing wrappers that we currently use? Thanks.

1 Like

Constrained existential still lack a key feature that would allow them to completely replace most hand-implemented type erasers: they don’t conform to the protocol of which they’re an existential.

So you couldn’t have a Set<any Hashable>, or return any View from var body, because those require Hashable and View conformance. Whereas AnyHashable and AnyView do conform to those protocols.

The reason for this is that the protocol conformance needs some business logic to be implemented. To give any Comparable a Comparable conformance, you need to implement <. You could imagine a hypothetical syntax where you could write extension any Comparable: Comparable { /* implement == and < */ } but a human has to specify the implementation (which could first compare the types, then open the values when the types are equal and compare those).

13 Likes

Is this correct then:

  • some Foo - static type, guaranteed to conform to Foo
  • any Foo - dynamic type, guaranteed not to conform to Foo (unless Foo is an @_marker protocol or an Obj. C protocol without static and/or initializer requirements)

Is that accurate or not?

any Error conforms to Error, through special hardcoding today. I don’t think nonconformance is a fundamental property of any Foo, just that today most any Foo do not conform to Foo. In some cases there are conformances that make sense but can’t be implemented today; in some a conformance wouldn’t make sense at all.

6 Likes

Is there a way to hardcode conformance, e.g.:

protocol Foo {}

extension any Foo: Foo {}

?

Not today; that would need to go through the Swift evolution process. Some questions that would have to be addressed in such a pitch:

  • How does this interact with SE-0352 Implicitly Opened Existentials?
  • Can such a conformance be added in a later OS release than the original protocol?
  • What happens if the any conformance is inconsistent with the conformance on the concrete type, and how do we teach that?
1 Like

Thank you! I had discovered this by successfully trying to use the type Result<Int, any Error>, but then discovered that I can’t use existentials in that way with any of my own protocols, and I was wondering about this. Are there any other hard coded conformances for specific existentials?

I believe that @objc protocols without static or initializer requirements get this behavior too.

1 Like

Technically, not hardcoded for a specific existential :wink:

Depending on what one considers to be “hardcoded” and “specific,” it bears mentioning that any Sendable conforms to Sendable. From the implementation perspective, this is made possible because Sendable is a @_marker protocol, not because the compiler is looking for Sendable by name. However, since @_marker is currently a compiler-internal feature and Sendable is the only @_marker protocol that Swift makes available, from the perspective of the public user-facing language, it is a hardcoded behavior specifically for Sendable implemented in the compiler.

However, if the meaning of “hardcoded” and “in the compiler” is meant in the sense of whether an end user “can” implement protocols with the same behavior, then for the most literal meaning of “can,” it is true that a user can ignore advice not to use compiler-internal features and create their own @_marker protocol, afaik, as I don’t think the compiler restricts its use to the standard library.

5 Likes