Swift. Generics

// Generic functionfunc swapTwoValues<T>(_ first: inout T, _ second: inout T) {
let temp = first
first = second
second = temp
}
var firstInt = 3
var secondInt = 5
swapTwoValues(&firstInt, &secondInt)
var firstString = "hello"
var secondString = "world"
swapTwoValues(&firstString, &secondString)
func someFunc<T, U, V>(_ first: T, _ second: U) -> V { ... }
// Generic custom typestruct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
var stringStack = Stack<String>()
stringStack.push("one")
stringStack.push("two")
var intStack = Stack<Int>()
intStack.push(1)
intStack.push(2)
extension Stack {
var topItem: Element? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
func someFunc<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {     
...
}
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
for (index, value) in array.enumerated() {
// requires conformance to Equatable
if value == valueToFind {
return index
}
}
return nil
}
// Generic protocolprotocol SomeProtocol {
associatedtype T
func someFunc(_ value: T)

associatedtype U
func anotherFunc(_ value: U)
}
class SomeClass: SomeProtocol {
typealias T = Int
func someFunc(_ value: Int) { print(value) }

typealias U = String
func anotherFunc(_ value: String) { print(value) }
}
class SomeClass: SomeProtocol {
func someFunc(_ value: Int) { print(value) }
func anotherFunc(_ value: String) { print(value) }
}
protocol SomeProtocol {
associatedtype U
func someFunc(_ value: U)
}
class SomeClass<T> { }extension SomeClass: SomeProtocol {
func someFunc(_ value: T) { }
}
protocol SomeProtocol {
associatedtype T: AnotherProtocol
func someFunc(_ value: T)
}

Generic Where Clause

// "Conformance to a protocol" example
class SomeClass<T> where T: SomeProtocol { }
...// "Inheritance from a class" example
protocol SomeProtocol {
associatedtype U where U: SomeClass
}
...// "Types must be the same or equal to a specific type" example
// U is associated type
func someFunc<T1: SomeProtocol, T2: SomeProtocol>(t1: T1, t2: T2) where T1.U == T2.U, T1.U == Int { }
extension Stack where Element: Equatable {
func isTop(_ item: Element) -> Bool {
guard let topItem = items.last else {
return false
}
return item == topItem
}
}

Under the Hood

Runtime Way

func test<T>(value: T) -> T {
let copy = value
print(copy)
return copy
}
// kind of runtimelet myStruct = MyStruct()
test(value: myStruct, metadata: MyStruct.metadata)
// muStruct value is stored in value buffer
// kind of runtimefunc test<T>(value: MyStruct<T>, tMetadata: T.Type) {
let myStructMetadata =
get_generic_metadata(MyStruct.metadataPattern, tMetadata)
}
let myStruct = MyStruct<Int>()
test(value: myStruct, tMetadata: Int.metadata)
func compareAndDraw<T>(first: T, second: T)
where T: Equatable, T: Drawable {
if first == second { // Equtable
first.draw() // Drawable
second.draw() // Drawable
}
}
compareAndDraw(first: MyStruct(), second: MyStruct())
// kind of runtimecompareAndDraw(first: MyStruct(),
second: MyStruct(),
metadata: MyStruct.metadata,
myStructIsEquatable: Equatable.pwt,
myStructIsDrawable: Drawable.pwt)

Compile-time Way or Specialization

func isEqual<T: Equatable>(first: T, second: T) -> Bool {
return first == second
}
isEqual(first: 1, second: 2)
isEqual(first: "one", second: "one")
isEqual(first: MyStruct(), second: MyStruct)
// kind of runtimeisEqual(first: Int, second: Int) { ... }
isEqual(first: String, second: String) { ... }
isEqual(first: MyStruct, second: MyStruct) { ... }
  • there are no existential containers and, as a result, no extra heap allocations (if using structures)
  • instead of having Table Dispatch any time when calling a protocol method, there can be used Direct Dispatch, because of taking a structure as a parameter type
function draw(copy: Drawable) {
copy.draw()
}
draw(copy: MyStruct()) // always Table Dispatch (PWT)// vsfunction draw<T: Drawable>(copy: T) {
copy.draw()
}
draw(copy: MyStruct()) // Direct Dispatch
// because at runtime it will be
// function draw(copy: MyStruct) { ... }
function draw(copy: MyStruct) {
copy.draw()
}
// before inlining code
draw(copy: MyStruct())
// after inlining code
MyStruct().draw()
struct Pair {
var first: Drawable
var second: Drawable
}
Pair(first: MyStruct(), second: MyStruct())// vsstruct Pair<T: Drawable> {
var first: T
var second: T
}
Pair(first: MyStruct(), second: MyStruct()) // more efficient

Resources

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store