This story will be a little long. Please bear with me. I will make sure I transfer the best knowledge about generics here. By the end of this story, we will learn the following things: 1. How to write generic code 2. How to constraint generics with protocols 3. How to use Equatable, Comparable and Hashable protocols 4. How to create highly reusable types
Lets Start!
What's Generics?
Before we learn what Generics is. We will see what the Polymorphism concept is. Decoding the word meaning of Polymorphism, we can understand that Polymorphism means:
Poly = Many
Morphe = Forms
So Polymorphism is equal to Many Forms.
For example, we will consider the scenario of Locking a door. How many ways can we lock a door?



Manual Key Lock, Biometric Lock, Automatic Lock etc. There are lot many ways to Lock a door. But the result is always the same a locked door or the process is locking the door. In programming, we can write a central Locking function irrespective of the type of lock. No matter whatever kind of Locking comes your way, your central Locking function will take care. And that's what Polymorphism means. In short, we can say that Polymorphism is a way of allowing similar operations to be grouped under the same name. Inheritance lets us inherit attributes and methods from another class. Polymorphism uses those methods to perform different tasks.
Generics are vital in creating polymorphic code. Once you start using Generics you will see your codebase slimming down, and you will write very elegant and highly reusable code.
Let's create our first Generic function.
We will first write a simple non-generic function to find the first and last items of an array.
Here you can see we have a function getFirstLast which takes an Integer array input and returns a tuple with the first element in the tuple as the first element of the array and the second element in the tuple as the last element of the array.
Now let's say your requirement has been tweaked a bit, and instead of the Integer array input, we will have a String array as the input and (String, String ) as the function return. How will you handle this? You will have to write a different Function with [String] input and (String, String) output.
Generics come to the rescue in these scenarios. How Generics can help us here, I will show you.
Converting a non-generic function to a generic function is a simple process.
We will add a generic <T> type parameter to our function parameter. Now once we have introduced a generic to our function, we can easily refer to it in the rest of the function.
Example: array: [T] and its return type (T, T)
Here T is just an abstract. We use T, U or V etc., as a convention. You can use any words instead of T.
Now we will see how our generics function works with other types.
Results:
Nikhil Vinod
1 2
1.0 2.0Generics with Custom Types
We can pass custom types too in Generics. For Eg
Result:
Dog(name: "Snow") Dog(name: "Snowy")Unlike Any there's no downcasting at runtime. The compiler declares everything at compile time itself.
Constraining Generics with Protocols
We saw how a simple T could be anything and everything. This means that our T The above examples are unconstrained generics. We can limit what a generic should represent by constraining it with a protocol.
For example:
Let's write a function to return the lowest value in an input array. Now that we have learnt how to write generic code, we will write something like the below.
But unfortunately, our code will throw an error.
error: binary operator '<' cannot be applied to two 'T' operands
return first < secondIt's because our T is not mature enough to perform a comparison operation. We will fix this issue using Protocols.
Equatable, Comparable and Hashable Protocols
What are Protocols ? Protocols are nothing but a set of rules. So how can a protocol help us in completing our lowest function? A protocol defines rules for the Generics to work. A class conforming to a Protocol needs to have all the required functions and variables of that protocol to be defined in the class. This is a known fact for us. Same here also we will define Protocols for the T in our lowest function to get it completed.
Here in the lowest function the problem is that comparator isn't working with T . So we have a Comparable protocol for the rescue.
Comparable
The comparable protocols have few static functions for the following operations: < <= >= >
Now to complete our lowest function. We will give booster supplements to lowest by making T conform to Comparable
Now that we know that all the items in the input array can be compared we can thus complete our lowest function by writing the below code:
Equatable
The equatable protocol allows us to check if two types are equal.
Equatable protocols have a static function for the operation == and it's as simple as the Comparable Protocols which we saw earlier.
For example:
We will try to create a struct which can take in an integer input as a value. We will also create few struct objects with some values are check if they are equal. If we do == comparison on the objects we with a non-generic struct then the compiler will throw an error. Lets make it conform to an Equatable protocol.
Here the AlienInteger is conformed to Equatable protocol we can use the == operator.
Result:
true, falseHashable
Hashable protocol defines a method that accepts a hasher to produce an integer hash value. The Generic with the Hashable type can then feed values to the hasher.
Hashable types can be used as dictionary keys or as part of a Set.
Many built-in types which are Equatable are also Hashable .
When you define an enumeration without associated values, it gains Hashable conformance automatically, and you can add Hashable conformance to your other custom types by implementing the hash(into:) method.
For structs whose, stored properties are all Hashable, and for enum types that have all-Hashable associated values, the compiler is able to provide an implementation of hash(into:) automatically.
For example: A teacher wants to input details of his/her class students into her class DB from a big list which has duplicates as well. All students will be having a unique ID. Now the teacher has to input unique students into the new list. Let's solve this problem.
To solve the problem of duplication we will maintain an Set of Students. Because the Student type is hashable, it can be used in a set.
We have made Student type as Hashable by providing an == operator function and also implementing the hash(into:) method.
The hash(into:) method in this example feeds the Student's name and id properties into the provided hasher. These properties are the same ones used to test for equality in the == operator function. Now that Student conforms to the Hashable protocol, we can create a set of currentList.
Result:
New student added (Snow, 2).We have come to the end of this story. I hope you have understood the Generics and Polymorphism concepts. Next up will be Generics and Subtypes — Subtyping and Invariance concepts in Swift
If you like this story please share and follow for more stories like these. Do let me know if you have any queries or suggestions. Thanks for reading ❤️