Generic type resolution in Swift programming

  • 2020-05-05 11:40:16
  • OfStack

Generic code allows you to write flexible and reusable functions and types that are tailored to your needs and applicable to any type. It allows you to avoid duplicate code and express the intent of the code in a clear and abstract way.
 
Generics are one of the powerful features of Swift, and many of the Swift standard libraries are built from generic code. In fact, generics are used throughout the language manual, but you just don't see them. For example, Swift's array and dictionary types are both generic sets. You can create an Int array, an String array, or even any other Swift type array. Similarly, you can create dictionaries that store any specified type (dictionary), and these types can be unlimited.
 
The problem solved by generics is
here is a standard, non-generic function swapTwoInts that swaps two Int values:


func swapTwoInts(inout a: Int, inout b: Int)
    let temporaryA = a
    a = b
    b = temporaryA
}

This function USES the write read (in-out) parameter to exchange the values of a and b, refer to the write read parameter.
 
The swapTwoInts function can swap the original values of b to a, or a to b. You can call this function to swap the original values of Int:

var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// The output "someInt is now 107, and anotherInt is now 3"
swapTwoInts The function is very useful, but it can only be swapped Int Value, if you want to swap two String or Double , you have to write more functions, such as swapTwoStrings and swapTwoDoublesfunctions As shown below:
func swapTwoStrings(inout a: String, inout b: String) {
    let temporaryA = a
    a = b
    b = temporaryA
}
 
func swapTwoDoubles(inout a: Double, inout b: Double) {
    let temporaryA = a
    a = b
    b = temporaryA
}

You may notice that the swapTwoInts, swapTwoStrings, and swapTwoDoubles functions all have the same functionality, except that the variables passed in are of different types: Int, String, and Double.
 
In practice, however, you often need a more powerful function that allows for as much flexibility as possible. A single function can be used to exchange two values of any type. Fortunately, generic code solves this problem for you. (one of these generic functions is already defined.)
 
Note: a and b are of the same type in all three functions. If a and b are not of the same type, they are not interchangeable. Swift is a type-safe language, so it does not allow a variable of type String to exchange values with a variable of type Double. If you must, Swift will report a compilation error.

generic function: type parameter
The
generic function can access any data type, such as 'Int' or 'String'.


func exchange<T>(inout a: T, inout b: T) {
   let temp = a
   a = b
   b = temp
} var numb1 = 100
var numb2 = 200 println("Before Swapping Int values are: \(numb1) and \(numb2)")
exchange(&numb1, &numb2)
println("After Swapping Int values are: \(numb1) and \(numb2)") var str1 = "Generics"
var str2 = "Functions" println("Before Swapping String values are: \(str1) and \(str2)")
exchange(&str1, &str2)
println("After Swapping String values are: \(str1) and \(str2)")

When we run the above program with playground, we get the following result


Before Swapping Int values are: 100 and 200
After Swapping Int values are: 200 and 100
Before Swapping String values are: Generics and Functions
After Swapping String values are: Functions and Generics

The function exchange() is used to exchange its description in the above scenario with < T > Is used as a type parameter value. For the first time, the function exchange() is called to return the Int value, and the second time the function exchange() is called to return the String value. Multiparameter types can include comma-separated Angle brackets.

Type parameters are named user-defined to understand the purpose of having type parameters. Provide < Swift T > The name of a generic type parameter. But type arrays and dictionary parameters can also be named keys, values, to make sure that their input belongs to the "dictionary."

generic type


struct TOS<T> {
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
  
   mutating func pop() -> T {
      return items.removeLast()
   }
} var tos = TOS<String>()
tos.push("Swift")
println(tos.items) tos.push("Generics")
println(tos.items) tos.push("Type Parameters")
println(tos.items) tos.push("Naming Type Parameters")
println(tos.items)
let deletetos = tos.pop()

When we run the above program with playground, we get the following result


[Swift]
[Swift, Generics]
[Swift, Generics, Type Parameters]
[Swift, Generics, Type Parameters, Naming Type Parameters]

extends the generic type
The
extension stack property is known to contain the "extension" keyword at the top of the item.


struct TOS<T> {
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }    mutating func pop() -> T {
      return items.removeLast()
   }
} var tos = TOS<String>()
tos.push("Swift")
println(tos.items) tos.push("Generics")
println(tos.items) tos.push("Type Parameters")
println(tos.items) tos.push("Naming Type Parameters")
println(tos.items) extension TOS {
   var first: T? {
      return items.isEmpty ? nil : items[items.count - 1]
   }
} if let first = tos.first {
   println("The top item on the stack is \(first).")
}

When we run the above program with playground, we get the following result


[Swift]
[Swift, Generics]
[Swift, Generics, Type Parameters]
[Swift, Generics, Type Parameters, Naming Type Parameters]

The item named type parameter at the top of the stack.

type constraints
The
Swift language allows "type constraints" to specify whether type parameters are inherited from a particular class, or to ensure protocol conformance standards.


func exchange<T>(inout a: T, inout b: T) {
   let temp = a
   a = b
   b = temp
} var numb1 = 100
var numb2 = 200 println("Before Swapping Int values are: \(numb1) and \(numb2)")
exchange(&numb1, &numb2)
println("After Swapping Int values are: \(numb1) and \(numb2)")   
var str1 = "Generics"
var str2 = "Functions" println("Before Swapping String values are: \(str1) and \(str2)")
exchange(&str1, &str2)
println("After Swapping String values are: \(str1) and \(str2)")

When we run the above program with playground, we get the following result


Before Swapping Int values are: 100 and 200
After Swapping Int values are: 200 and 100
Before Swapping String values are: Generics and Functions
After Swapping String values are: Functions and Generics

association type
Swift allows related types and can be internally declared by the keyword "typealias" protocol.


protocol Container {
   typealias ItemType
   mutating func append(item: ItemType)
   var count: Int { get }
   subscript(i: Int) -> ItemType { get }
} struct TOS<T>: Container {
   // original Stack<T> implementation
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }
  
   mutating func pop() -> T {
      return items.removeLast()
   }    // conformance to the Container protocol
   mutating func append(item: T) {
      self.push(item)
   }
  
   var count: Int {
      return items.count
   }    subscript(i: Int) -> T {
      return items[i]
   }
} var tos = TOS<String>()
tos.push("Swift")
println(tos.items) tos.push("Generics")
println(tos.items) tos.push("Type Parameters")
println(tos.items) tos.push("Naming Type Parameters")
println(tos.items)

When we run the above program with playground, we get the following result


[Swift]
[Swift, Generics]
[Swift, Generics, Type Parameters]
[Swift, Generics, Type Parameters, Naming Type Parameters]

Where clause
The
type constraint enables users to define parameter requirements for a type associated with a generic function or type. The 'where' clause used to define the related type is declared as part of the requirements of the type parameter list. The "where" keyword type parameter is placed after the restriction of the related types between the types and the related types, and after the list of equal relations.


 protocol Container {
   typealias ItemType
   mutating func append(item: ItemType)
   var count: Int { get }
   subscript(i: Int) -> ItemType { get }
} struct Stack<T>: Container {
   // original Stack<T> implementation
   var items = [T]()
   mutating func push(item: T) {
      items.append(item)
   }    mutating func pop() -> T {
      return items.removeLast()
   }    // conformance to the Container protocol
   mutating func append(item: T) {
      self.push(item)
   }
  
   var count: Int {
      return items.count
   }    subscript(i: Int) -> T {
      return items[i]
   }
} func allItemsMatch<
   C1: Container, C2: Container
   where C1.ItemType == C2.ItemType, C1.ItemType: Equatable>
   (someContainer: C1, anotherContainer: C2) -> Bool {
   // check that both containers contain the same number of items
   if someContainer.count != anotherContainer.count {
      return false
} // check each pair of items to see if they are equivalent
for i in 0..<someContainer.count {
   if someContainer[i] != anotherContainer[i] {
      return false
   }
}
      // all items match, so return true
      return true
} var tos = Stack<String>()
tos.push("Swift")
println(tos.items) tos.push("Generics")
println(tos.items) tos.push("Where Clause")
println(tos.items) var eos = ["Swift", "Generics", "Where Clause"]
println(eos)

When we run the above program with playground, we get the following result


[Swift]
[Swift, Generics]
[Swift, Generics, Where Clause]
[Swift, Generics, Where Clause]


Related articles: