When is `any` mandatory when creating a variable conforming to a protocol

Hi everyone,

I'm confused about when to use any when I have a variable that can contain instances of multiple types, as long as they conform to a specific protocol. (I believe this is called an existential type, but not sure).

Take this example from @Douglas_Gregor's excellent series* Swift for C++ Practitioners:

The example code is:

protocol Quantifiable {
  func cost() -> Double
}

var q: any Quantifiable = 1   // okay, Int conforms to Quantifiable
print(q.cost())               // can use the members of the Quantifiable protocol

q = "Hello"   

However, this code also works, and seems to be functionally equivalent:

var q: Quantifiable = 1   // okay, Int conforms to Quantifiable
print(q.cost())               // can use the members of the Quantifiable protocol

q = "Hello"  
print(q.cost())               // can use the members of the Quantifiable protocol

I understand that adding any was added in Swift 5.7, but I've tested using Swift 5.10 and don't get a compiler warning/error. Code just runs.

In practice, I always use the var foo: MyProtocol = ... and that seems to work fine.

So, when would you need to add the any keyword?

Thanks in advance!

KR Maarten

* I enjoy this series a lot, even though I'm anything but a C++ Practitioner. :star_struck:

any is mandatory if the protocol has any associated types.

1 Like

Before Swift 5.7, the spelling for any P was just P. Note you could only reference protocols without associated types or Self requirements like this until Swift 5.7 lifted this. To preserve source compatibility with these simpler protocols in older code, P is still accepted in the Swift 5 language version but requires any P for protocols like Equatable, etc.. any P will be required for all protocols with the Swift 6 language version mode iirc.

2 Likes

@jlukas @Alejandro thank you for the quick and clear replies. I understand now.

This also means that if I want to future proof my code for Swift 6, I should get into the habit of using any P starting today.

That was the original plan, but the decision was later reverted. The any P syntax will not be required for existentials in the Swift 6 language mode.

Holly Borla, Progress toward the Swift 6 language mode (2023-11-07):

The Language Steering Group has decided that one previously-accepted upcoming feature, ExistentialAny, will not be enabled by default in Swift 6. SE-0335: Introduce existential any introduces the any keyword to identify existential types. The proposal also specifies that "bare" protocol names will no longer be permitted as types---they must either use any or some, as appropriate---under the upcoming feature flag ExistentialAny. Given the concerns about migration to consistent use of existential any in the language, and the expectation that additional language improvements will be coming that might affect the end result of that migration, the Language Steering Group is deferring the source-incompatible changes in SE-0335 to a future language revision.

I would still suggest getting into the habit of writing any P everywhere because it's the better syntax IMO:

  • One consistent spelling for all existentials
  • Using different spellings for when a protocol is used as a constraint (no any) vs. when it's used as an existential is a good thing
  • Nice congruence between the any P and some P spellings, which can be used in similar places (though note that there have been discussions to make the plain P spelling actually mean some P in a future language version because some P should generally be preferred unless you explicitly need any P)
6 Likes

I do the same, and even enforce this with the ExistentialAny upcoming feature flag. In Package.swift, I write:

import PackageDescription

// Regular package description
let package = Package(...)

// `any P` everywhere
for target in package.targets {
    var settings = target.swiftSettings ?? []
    settings.append(contentsOf: [
        // <https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md>
        .enableUpcomingFeature("ExistentialAny"),
    ])
    target.swiftSettings = settings
}

In Xcode targets, I setup a xcconfig file that contains:

// <https://github.com/apple/swift-evolution/blob/main/proposals/0335-existential-any.md>
OTHER_SWIFT_FLAGS = $(inherited) -enable-upcoming-feature ExistentialAny

I remember my initial reluctance for prefixing all protocols with any, particularly because of any Error which is ubiquitous, and the vexing optional (any P)? that requires parenthesis (the plain any P? would be just as good). But I came up with the same conclusions as @ole. I'll add two cents:

  • Using any is a way to bring the topic of type erasure to the consciousness. It's not always necessary, but well at some point a Swift developer has to understand this. (Unrelated note: @Douglas_Gregor was able to avoid the word "existential" in his blog post, and this is remarkable.)
  • When I read func f(x: any P), I want to turn it into func f(x: some P). On the other side, func f(x: P) does not trigger me at all. To make my point more clear: not all protocols are named in a way that makes it obvious that they are protocols. For them, the shorthand notation P does not make it obvious that some P could avoid an unnecessary boxing and allow specialization in performance critical paths. In func draw(shape: Shape), what says that Shape is a protocol instead of, say, an enum?
4 Likes