|
1 | 1 | # Protocol type not conforming to itself
|
2 |
| -Swift disallows us from using a protocol as a type that conforms to itself as illustrated in the examples below |
| 2 | +Protocols in Swift may be used as types. Protocols as types are sometimes called existential types. |
3 | 3 |
|
4 |
| -```swift |
5 |
| -protocol SomeProtocol { |
6 |
| - init() |
7 |
| -} |
8 | 4 |
|
9 |
| -struct SomeStruct: SomeProtocol {} |
10 |
| -struct AnotherStruct: SomeProtocol {} |
| 5 | +```swift |
| 6 | +protocol P {} |
11 | 7 |
|
12 |
| -var arr: [SomeProtocol] = [SomeStruct(), AnotherStruct()] |
13 |
| -arr.appendNewItem() |
| 8 | +struct S: P {} |
14 | 9 |
|
15 |
| -extension Array where Element: SomeProtocol { |
16 |
| - mutating func appendNewItem() { |
17 |
| - append(Element()) |
18 |
| - } |
19 |
| -} |
| 10 | +var s: P = S() // This creates existential type because the protocol P is used as a type |
20 | 11 | ```
|
21 | 12 |
|
22 |
| -The code snippet above would not compile because we are using `SomeProtocol` as a type that conforms to itself. There is no concrete implementation for the protocol. |
23 |
| - |
24 |
| -Consider also the case of using protocol as a type in a generic type - |
| 13 | +However, a protocol type does not conform to protocols - not even the protocol itself. |
| 14 | +Allowing existential types to conform to protocols is unsound because some protocol with static methods, initializers, or associated types requirements cannot be accessed from the protocol type itself - these kinds of requirements require a concrete type. |
| 15 | +Let's walk through the example below: |
25 | 16 |
|
26 | 17 | ```swift
|
27 |
| -protocol AnotherProtocol { |
28 |
| - static func foo() |
| 18 | +protocol Word: Hashable { |
| 19 | + var word: String { get } |
29 | 20 | }
|
30 | 21 |
|
31 |
| -struct GenericStruct<T: AnotherProtocol> { |
32 |
| - func faz() { |
33 |
| - T.foo() |
34 |
| - } |
| 22 | +struct Singular: Word { |
| 23 | + var word: String |
35 | 24 | }
|
36 | 25 |
|
37 |
| -GenericStruct<AnotherProtocol>().faz() |
38 |
| -``` |
39 |
| -Constructing the instance of the struct `GenericStruct` with type `AnotherProtocol` will not compile because there is no concrete implementation for the static requirement of the protocol. |
40 |
| -There is no implementation for for() used above. |
41 |
| - |
42 |
| -We, however have an exception for `@objc` protocols that conforms to itself as shown below |
43 |
| - |
44 |
| -```swift |
45 |
| -import Foundation |
46 |
| - |
47 |
| -@objc protocol SomeProtocol { |
48 |
| - func foo() |
| 26 | +struct Plural: Word { |
| 27 | + var word: String |
49 | 28 | }
|
50 | 29 |
|
51 |
| -class SomeClass : SomeProtocol { |
52 |
| - func foo() { |
53 |
| - print("foo called") |
54 |
| - } |
55 |
| -} |
| 30 | +let singularWord = Singular(word: "mango") |
| 31 | +let pluralWord = Plural(word: "mangoes") |
56 | 32 |
|
57 |
| -func faz<T : SomeProtocol>(_ t: T) { |
58 |
| - t.foo() |
59 |
| -} |
| 33 | +let wordPairDict: [Word: Word] = [singularWord: pluralWord] // Error |
| 34 | +``` |
| 35 | + |
| 36 | +One workaround to fix this problem is to write a type erasure for the protocol `Word`. Think of type erasure as a way to hide an object's type. Since `Word` is of type `Hashable`, we already have `AnyHashable` type erasure available in the standard library which we can easily use here. |
60 | 37 |
|
61 |
| -let c: SomeProtocol = SomeClass() |
62 |
| -faz(c) |
| 38 | +```swift |
| 39 | +// The fix |
| 40 | +let wordPairDict: [AnyHashable: AnyHashable] = [singularWord: pluralWord] |
63 | 41 | ```
|
64 | 42 |
|
65 |
| -The function `faz` requires that `T` conforms to `SomeProtocol` and we can easily substitute in `SomeProtocol` for `T` because it has no static requirements. |
| 43 | +# Exceptions |
| 44 | +`@objc` protocol type with no static requirements however do conform to its own protocol. One example is the `Error` protocol. |
66 | 45 |
|
0 commit comments