Swift Difference between ampersand and comma when conforming to protocols

Viewed 72

I was reading documentation about Swift Protocol Composition and found this: https://docs.swift.org/swift-book/LanguageGuide/Protocols.html#:~:text=Reference%20Types.-,Protocol%20Composition,-It%20can%20be

It can be useful to require a type to conform to multiple protocols at the same time. You can combine multiple protocols into a single requirement with a protocol composition. Protocol compositions behave as if you defined a temporary local protocol that has the combined requirements of all protocols in the composition. Protocol compositions don’t define any new protocol types.

Protocol compositions have the form SomeProtocol & AnotherProtocol. You can list as many protocols as you need, separating them with ampersands (&). In addition to its list of protocols, a protocol composition can also contain one class type, which you can use to specify a required superclass.

Here’s an example that combines two protocols called Named and Aged into a single protocol composition requirement on a function parameter:

protocol Named {
    var name: String { get }
}
protocol Aged {
    var age: Int { get }
}
struct Person: Named, Aged {
    var name: String
    var age: Int
}
func wishHappyBirthday(to celebrator: Named & Aged) {
    print("Happy birthday, \(celebrator.name), you're \(celebrator.age)!")
}
let birthdayPerson = Person(name: "Malcolm", age: 21)
wishHappyBirthday(to: birthdayPerson)
// Prints "Happy birthday, Malcolm, you're 21!"

Could someone maybe explain the difference between the ampersand and the comma when stating that a type conforms to two or more protocols? I tested this in Playgrounds and both the ampersand and comma worked just fine. I tried doing some research online but didn't really find a good answer for this..

1 Answers

You use the , in type declarations, like your struct Person: Named, Aged.

But for argument lists and other contexts, the , already is a separator. So to specify a type that means "must conform both to protocol Foo and protocol Bar" a different character is necessary. That's why you need to write func wishHappyBirthday(to celebrator: Named & Aged). The same is true for writing variables, like let anotherCelebrator: Named & Aged = …. You can also do type aliases like this: typealias NamedAndAged = Named & Aged.

So the question is, why don't we use the & when declaring a type? I think the answer is history, as far I remember protocol composition wasn't available in Swift 1 (couldn't find when it was introduced; Swift 4?). And it turn out you can use & instead in a lot of cases:

struct Foo: Codable & Identifiable {
    var id: UUID { UUID() }
}
        
class Bar: NSObject & Codable {
}

It does not work with the first and second type of an enum: enum Baz: Int & CaseIterable { … } yields a compiler error, but enum Baz: Int, CaseIterable & Identifiable { … } is allowed.

Related