Как в Swift создать коллекцию для хранения элементов разных типов?

Ответ

Типизированный массив [Element] в Swift хранит элементы одного типа. Для гетерогенных коллекций используйте следующие подходы, от наиболее к наименее предпочтительному:

1. Enum с ассоциированными значениями (типобезопасный и рекомендуемый)

enum HeterogeneousValue {
    case integer(Int)
    case text(String)
    case floatingPoint(Double)
    case boolean(Bool)
}

let collection: [HeterogeneousValue] = [
    .integer(42),
    .text("Hello"),
    .floatingPoint(3.14),
    .boolean(true)
]

for item in collection {
    switch item {
    case .integer(let value): print("Int: (value)")
    case .text(let value): print("String: (value)")
    case .floatingPoint(let value): print("Double: (value)")
    case .boolean(let value): print("Bool: (value)")
    }
}

2. Протоколы (когда у элементов общая функциональность)

protocol Renderable {
    func render() -> String
}

struct Label: Renderable { func render() -> String { "Label" } }
struct Button: Renderable { func render() -> String { "Button" } }

let uiElements: [Renderable] = [Label(), Button()]
uiElements.forEach { print($0.render()) }

3. Тип Any (используйте с осторожностью)

let mixedArray: [Any] = [1, "Text", 3.14, true]

for element in mixedArray {
    switch element {
    case let intValue as Int: print("Integer: (intValue)")
    case let stringValue as String: print("String: (stringValue)")
    case let doubleValue as Double: print("Double: (doubleValue)")
    case let boolValue as Bool: print("Bool: (boolValue)")
    default: print("Unknown type")
    }
}

Рекомендации:

  • Предпочитайте enum для фиксированного набора типов — это обеспечивает безопасность типов и исчерпывающую обработку в switch.
  • Используйте протоколы, когда важна общая поведенческая контракт.
  • Избегайте Any — он обходит систему типов Swift, требует ручных проверок и подвержен ошибкам времени выполнения.

Ответ 18+ 🔞

А, ну вот, опять про эти ваши массивы в Swift, блядь. Сидишь такой, пишешь код, а тут — бац! — и надо запихнуть в одну кучу и числа, и строки, и ещё какую-нибудь хуйню. Обычный-то массив [Element] на такое скажет: «Иди нахуй, чувак, я с одним типом работаю». Ну и правильно, блядь, он же типизированный, не распиздяйство какое.

Но выходы есть, ёпта. Вот тебе три штуки, от самого годного до такого, от чего сам Стив Джобс в гробу перевернулся бы.

1. Enum — наш царь и бог, типобезопасность ебать

Это когда ты заранее знаешь, какие типы тебе могут понадобиться. Красота, блядь, а не подход. Создаёшь enum и на каждый случай вешаешь ассоциированное значение — как рюкзачок для данных.

enum HeterogeneousValue {
    case integer(Int)
    case text(String)
    case floatingPoint(Double)
    case boolean(Bool)
}

let collection: [HeterogeneousValue] = [
    .integer(42),
    .text("Hello"),
    .floatingPoint(3.14),
    .boolean(true)
]

for item in collection {
    switch item {
    case .integer(let value): print("Int: (value)")
    case .text(let value): print("String: (value)")
    case .floatingPoint(let value): print("Double: (value)")
    case .boolean(let value): print("Bool: (value)")
    }
}

Вот видишь? Компилятор тебя за руку держит, не даёт наебнуться. Забыл кейс в свитче? Он тебе: «Э, бошка думай, не все случаи обработал, мудак!». Идеально.

2. Протоколы — когда всем надо делать одно и то же

Допустим, у тебя там UI элементы: лейблы, кнопки, свитчи. И всем им надо, например, отрендериться. Ну так объедини их протоколом, ёпта!

protocol Renderable {
    func render() -> String
}

struct Label: Renderable { func render() -> String { "Label" } }
struct Button: Renderable { func render() -> String { "Button" } }

let uiElements: [Renderable] = [Label(), Button()]
uiElements.forEach { print($0.render()) }

Все довольны, все рендерятся. Каждый объект — свой тип, но в массиве они все Renderable. Удобно, блядь, и логично.

3. Any — тёмная сторона, в которую лучше не совать свой хуй

А вот это уже, прости господи, чистое распиздяйство. Типа «закину всё в одну кучу, а там разберёмся». Swift на такое смотрит и тихо плачет в углу.

let mixedArray: [Any] = [1, "Text", 3.14, true]

for element in mixedArray {
    switch element {
    case let intValue as Int: print("Integer: (intValue)")
    case let stringValue as String: print("String: (stringValue)")
    case let doubleValue as Double: print("Double: (doubleValue)")
    case let boolValue as Bool: print("Bool: (boolValue)")
    default: print("Unknown type")
    }
}

Смотри, что происходит: ты теряешь всю безопасность типов нахуй. Добавил завтра какой-нибудь URL в массив — и получи default ветку, а то и креш, если неаккуратно кастовать будешь. Это как ходить по тонкому льду с Any вместо коньков — один хуй провалишься.

Короче, запомни, как «Отче наш»:

  1. Enum — когда типы известны и их список не меняется каждый день. Это топ, это наше всё.
  2. Протоколы — когда объекты разные, но делать они должны что-то общее.
  3. Any — используй только если тебя заставили под дулом пистолета, или ты реально хочешь себе проблем на ровном месте. Доверия к такому коду — ноль ебать.

Выбирай с умом, а не как последний говнокодер.