Как сделать типобезопасные индексы в Swift?

Я пытаюсь сделать что-то вроде этого:

typealias HumanId = Int 
typealias RobotId = Int

func getHuman(at index: HumanId) -> Human
func getRobot(at index: RobotId) -> Robot

но сейчас я могу звонить getHuman с RobotId просто отлично: getHuman(at: RobotId(0)).

Как сделать этот тип безопасным?


Я понимаю, что могу сделать что-то вроде:

struct HumanId { let id: Int }
struct RobotId { let id: Int }

... и некоторые дополнительные вещи, чтобы эти структуры функционировали как индексы, но это привело бы к некоторому дублированию кода, и, поскольку у меня есть более двух из этих типов идентификаторов, я хотел бы как-то сократить это с помощью typealiases и generics может быть, для того, чтобы сделать их уникальными?

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
0
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вы можете использовать дженерики Swift для достижения своей цели. Введите общий тип Index следующим образом:

struct Index<T>: RawRepresentable {
    let rawValue: Int
    init(rawValue: Int) { self.rawValue = rawValue }
    init(_ rawValue: Int) { self.rawValue = rawValue }
}

а затем используйте его следующим образом:

func getHuman(at index: Index<Human>) -> Human { ... }
func getRobot(at index: Index<Robot>) -> Robot { ... }

getHuman(at: Index(1))
getRobot(at: Index(2))

Буквенные индексы

Вы даже можете использовать протокол ExpressibleByIntegerLiteral, чтобы обеспечить некоторый синтаксический сахар при использовании литеральных индексов:

extension Index: ExpressibleByIntegerLiteral {
    public init(integerLiteral value: Int) { self.rawValue = value }
}

Например:

getHuman(at: 1)
getRobot(at: 2)

Но следующий код не будет собран, поэтому решение по-прежнему безопасно для типов:

let someIndex = 123
getHuman(at: someIndex)

error: cannot convert value of type 'Int' to expected argument type 'Index<Human>'

Сопоставимые индексы

Как было предложено в комментариях, мы также могли бы добавить соответствие Comparable (например, чтобы вы могли использовать структуру Index в качестве индекса в типе, соответствующем стандартному протоколу Collection):

extension Index: Comparable {
    static func < (lhs: Index, rhs: Index) -> Bool {
        lhs.rawValue < rhs.rawValue
    }
}

Пример:

Index<Human>(1) < Index<Human>(2) // true

Мне нравится элегантность этого ответа. Вы должны добавить соответствие Comparable, чтобы вы могли использовать тип в качестве индекса в типе, соответствующем Collection

JeremyP 15.05.2023 12:01

Другие вопросы по теме