Introduction
Generic code enables you to write flexible code, reusable functions, and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstract manner.
Generics are one of the powerful features of Swift, and much of the Swift standard library is built with generic code.
The Problem That Generics Solve
A nongeneric function called swapTwoInts(_:_), which swap two int values -
- func swapTwoInts(_ a: inout Int, _ b: inout Int){
- let temp = a
- a = b
- b = temp
- }
- var firstItem = 5
- var secondItem = 6
- swapTwoInts(&firstItem, &secondItem)
-
- print("First Item is now \(firstItem), and Second Item is now \(secondItem)")
-
- Output: First Item is now 6, and Second Item is now 5
The above function makes use of in-out parameters to swap the values a and b.
The swapTwoInts(_:_) function is useful, but It can only use for int values. If you want to swap two string values or double values, you have to write more functions such as swapTwoStrings(_:_) and swapTwoDoubles(_:_) functions like below -
- func swapTwoStrings(_ a: inout String, _ b: inout String){
- let temp = a
- a = b
- b = temp
- }
-
- func swapTwoDoubles(_ a: inout Double, _ b: inout Double){
- let temp = a
- a = b
- b = temp
- }
It's more useful, and considerably more flexible, to write a single function that swaps values of any type. Generic function enables to you write such types of functions.
Note: In all three functions, types of a and b must be of the same type. If a and b are not the same type, it is not possible to swap their values. Swift is a type-safe language and does not allow a variable of type string and a variable of the type of double to swap values to each other. Attempting to do so you will get a compile-time error.
Generics Functions
Generics functions can work with any type. Let's take a generic version of function swapTwoValues(_:_).
Example -
-
-
- func swapTwoValues<T>(_ a: inout T, _ b: inout T){
- let temp = a
- a = b
- b = temp
- }
-
- var p = 5
- var q = 6
- swapTwoValues(&p,&q)
- print("Value of P is now \(p) ,Value of Q is now \(q)")
-
- Output: Value of P is now 6, Value of Q is now 5
-
-
- var p = "Pravesh"
- var q = "Dubey"
- swapTwoValues(&p,&q)
- print("Value of P is now \(p) ,Value of Q is now \(q)")
-
- Output: Value of P is now Dubey, Value of Q is now Pravesh
The generic version of the function uses the placeholder type name called T instead of the actual type name (such as Int, String or Double).
The placeholder type name doesn't say anything about what T must be, but it does say that a and b must be the same type T, whatever type T represents.
The other difference between generic and non-generic functions is that the generic function's name followed by placeholder type name T inside a curly bracket (<T>). The brackets tell Swift that T is the placeholder type name within the function definition.
Type parameters
Type parameters specify name and placeholder type and are written immediately after function's name, between a pair of matching angle brackets (such as <T>). Once you specify a type parameter, you can use it to define the type of a function's parameters (such as a and b parameters of the swapTwoValues(_:_) function), or as the function's return type. In each case, the type parameter is replaced with the actual type whenever the function is called.
You can provide more than one type of parameter by writing multiple type parameter names within the angle brackets, separated by commas.
Generics Types
In generics functions, Swift enables you to define own generic types. These are custom classes, structures, and enumerations that can work any type, in a similar way to Array and Dictionary.
Let's take an example of Generics collection type stack. A stack is an ordered set of values. In the below example you'll see how to write a nongeneric version of a stack, in this stack for a stack of int values -
- struct IntStack{
- var items = [Int]()
- mutating func push(item : Int){
- items.append(item)
- }
- mutating func pop() -> Int{
- return items.removeLast()
- }
- }
Here's the nongeneric version of the same code -
- struct IntStack<Element>{
- var items = [Element]()
- mutating func push(item : Element){
- items.append(item)
- }
- mutating func pop() -> Element{
- return items.removeLast()
- }
- }
- var stackItems = IntStack<Any>.init()
- stackItems.push(item: 9.0)
- stackItems.push(item: "Pravesh")
- stackItems.push(item: 3)
- print(stackItems)
-
- Output : IntStack<Any>(items: [9.0, "Pravesh", 3])
In the above example Element is used as a placeholder name for a type.
Summary
I hope you understood, how to define generic functions and what is the benefit to define a generic type function.
If you enjoyed reading my article, please share it and recommend to others. Thank you in advance.
Happy programming!!!