Generics
is an excellent way of writing reusable functions for multiple types. If you are already a developer, you have already been using generics in your code. Dictionary and Array
is a great example of generics, we can define an array of any type, and that’s what makes it a generic. The flexibility of choosing a type for our Array
and Dictionaries
is the key functionality of using Generics
.
Let’s look at how a dictionary type is defined, and what happens if we try to push a value of some other type.
// Creating a Dictionary with String type as the KEY, and Int type as the VALUE
var dict: [String: Int] = ["One" : 1]
//The line below gives a Compile error: cannot assign value of type '[String]' to subscript of type 'Int'
dict["Two"] = ["II"]
The compiler gives an error as we tried to set a value of different type other than an Int
.
The type declared while initialization of the dictionary cannot be changed, though you have so many options, the most common choice is using Any
while declaring a type for value in Dictionary
. While working with dictionaries, we deal with multiple types of data in many cases.
Any
here helps us push data of all the types we have available on Swift, sounds so much like a generic, but it is not, as Generics are type-safe, and Any
is not.
Generic is very powerful, and we have not even started on it yet.
Let’s look at a non-generic function that squares our Int
values and then finally add them.
func squareAndAddInt(_ a: Int,_ b: Int) -> Int {
return (a * a) + (b * b)
}
let intResult = squareAndAdd(5, 10)
// Output = 125
If we want to do this with a value of type Double
, we’ll need to declare another function that takes two Double
values and returns a Double
.
func squareAndAddDouble(_ a: Double,_ b: Double) -> Double {
return (a * a) + (b * b)
}
If you look at the code, the code for square and add is exactly the same except for their types, and that’s where Generic
comes in to increase reusability in our code. Let me show you how we can write a generic function and achieve the same functionality.
func squareAndAdd<T>(_ a: T, _ b: T) -> T {
return (a * a) + (b * b)
}
T
here acts as a placeholder type for the values we are going to pass. So, if we pass Int
values, the T
here will act as a Int
, but there’s a problem here, the code doesn’t compile, and there’s a very good reason for this.
The operation done here is a mathematical operation, which cannot be done on values other than the numeric type. So what do we do?
// Conforming Generic type "T" to Numberic protocol
func squareAndAdd<T: Numeric>(_ a: T, _ b: T) -> T {
return (a * a) + (b * b)
}
let intResult = squareAndAdd(5, 10)
let doubleResult = squareAndAdd(5.5, 10.5)
/* Output:
intResult = 125
doubleResult = 140.5
*/
// Compile error: function 'squareAndAdd' requires that 'String' conform to 'Numeric'
let stringResult = squareAndAdd("one", "two")
If you look at the example above, and you can also try to execute this in the Playground
file. We are successfully able to get result for both Int
and Double
type. If we try to pass a String
value, the compiler gives an error stating that “squareAndAdd
requires that String
conform to Numeric
”.
This is how you can apply restrictions to types that can be passed in your generic function, and because of the function being generic, we don’t need to write the same code for all the other Numeric
types. There’s more to learn about Generics, I’ll post more articles on generic soon, so don’t forget to subscribe.
I hope you guys enjoyed reading my article. There’s a lot more to learn together, so subscribe to stay updated about my upcoming articles.
If you have any suggestions or questions, Feel free to connect me on Twitter or Reddit. 😉