Skip to content

Commit 42b9b48

Browse files
update educational notes
1 parent 434607d commit 42b9b48

File tree

1 file changed

+25
-46
lines changed

1 file changed

+25
-46
lines changed
Lines changed: 25 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,45 @@
11
# 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.
33

4-
```swift
5-
protocol SomeProtocol {
6-
init()
7-
}
84

9-
struct SomeStruct: SomeProtocol {}
10-
struct AnotherStruct: SomeProtocol {}
5+
```swift
6+
protocol P {}
117

12-
var arr: [SomeProtocol] = [SomeStruct(), AnotherStruct()]
13-
arr.appendNewItem()
8+
struct S: P {}
149

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
2011
```
2112

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:
2516

2617
```swift
27-
protocol AnotherProtocol {
28-
static func foo()
18+
protocol Word: Hashable {
19+
var word: String { get }
2920
}
3021

31-
struct GenericStruct<T: AnotherProtocol> {
32-
func faz() {
33-
T.foo()
34-
}
22+
struct Singular: Word {
23+
var word: String
3524
}
3625

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
4928
}
5029

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")
5632

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.
6037

61-
let c: SomeProtocol = SomeClass()
62-
faz(c)
38+
```swift
39+
// The fix
40+
let wordPairDict: [AnyHashable: AnyHashable] = [singularWord: pluralWord]
6341
```
6442

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.
6645

0 commit comments

Comments
 (0)