Для статически типизированных языков компилятор должен иметь всю информацию о классах и функциях во время компиляции. Это означает, что все переменные, константы и функции должны иметь заранее объявленные типы. Однако по-прежнему возможно иметь одну функцию или один метод, которые могут воздействовать на разные типы. Для этой цели Swift предоставляет мощную функцию Generic.

Общий код включает функции и типы, которые могут работать с разными типами.

В общем, функция обычно представляет собой фрагмент кода, который можно повторно использовать и использовать для выполнения одного связанного действия; поэтому обычная форма функции распространена следующим образом:

func function_name(parameters: declared_type) -> return_type {
    //... Code here
    return typed_constants
}

Традиционно мы могли создавать функции для одной и той же цели по отдельности для работы с разными типами. Однако это означает, что мы создаем коды на основе схожих шаблонов для каждого объявления уникального типа. И это каким-то образом нарушает кодирование чистым способом и без обслуживания, что всегда является целью и принципом для программистов. Ввиду этого нам нужен способ, который абстрагирован и способен выразить свое намерение в ясной форме. И это то, что мы назвали Generic.

Начнем с общей сущности, которую можно повторно использовать с разными типами.

Общий объект должен иметь параметр типа. «T» ниже обозначает любой тип, который в конечном итоге будет использоваться в общем объекте.

struct GenericStruct<T> {
    var someThing: T
}

// After above manner, GenericStruct can be constructed and parameterized over different types by adding type parameters in a pair of angle-brackets.
let a = GenericStruct<Int>(someThing: 1)
let b = GenericStruct<Float>(someThing: 2.5)
let c = GenericStruct<String>(someThing: "Hello, world!")

А что, если мы поместим общий объект в коллекцию?

Похоже, что определить общий объект легко и просто. Однако не всегда история будет такой простой. Если мы действительно хотим поместить нашу общую сущность в тип коллекции, такой как Set, нам нужно добавить еще несколько украшений и принять для этой цели протокол Hashable. Фрагмент ниже является исправленной версией.

struct GenericStruct<T: Hashable>: Hashable {
    var someThing: T
    
    var hashValue: Int {
        get {
            // Hashable in angle-brackets is a constraint to tell compiler that type T must have .hashValue
            return someThing.hashValue
        }
    }
    
    static func ==(lhs: GenericStruct, rhs: GenericStruct) -> Bool {
        return lhs.hashValue == rhs.hashValue
    }
}

let set: Set = [
    GenericStruct<String>(someThing: "Watermelon"),
    GenericStruct<String>(someThing: "Orange"),
    GenericStruct<String>(someThing: "Banana")
]

Функция расширения — последнее, что нельзя упустить

Возвращаясь к нашей теме, цель состоит в том, чтобы написать функцию расширения для агрегатов абстрактного типа. Нет сомнений, что история еще не закончена. Последняя часть функции расширения все еще отсутствует. Нам нужна третья версия фрагмента, чтобы наша функция расширения работала с GenericStruct.

extension Set {
    func getFirstWith<T: Hashable>(value: T) -> Element? {
        return filter({$0.hashValue == value.hashValue}).first
    }
}

let set: Set = [
    GenericStruct<String>(someThing: "Watermelon"),
    GenericStruct<String>(someThing: "Orange"),
    GenericStruct<String>(someThing: "Banana")
]

let output = set.getFirstWith(value: "Orange")
print(output!)

Конечно, другой тип, например Int, тоже работает. Ваше здоровье!

let set: Set = [
    GenericStruct<Int>(someThing: 226),
    GenericStruct<Int>(someThing: 603),
    GenericStruct<Int>(someThing: 818)
]
let output = set.getFirstWith(value: 818)
print(output!)