Я пытаюсь сделать что-то вроде этого:
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 может быть, для того, чтобы сделать их уникальными?
Вы можете использовать дженерики 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