如何在 Swift 中进行类型安全索引?

Pau*_*kis 4 generics collections type-safety type-alias swift

我正在尝试做这样的事情:

typealias HumanId = Int 
typealias RobotId = Int

func getHuman(at index: HumanId) -> Human
func getRobot(at index: RobotId) -> Robot
Run Code Online (Sandbox Code Playgroud)

但现在我可以getHumanRobotId很好的方式调用:getHuman(at: RobotId(0))

我如何使这种类型安全?


我知道我可以做类似的事情:

struct HumanId { let id: Int }
struct RobotId { let id: Int }
Run Code Online (Sandbox Code Playgroud)

...还有一些额外的东西可以使这些结构充当索引,但这会导致一些代码重复,并且由于我有超过 2 个这样的 id 类型,我想通过类型别名和泛型以某种方式缩短它也许是为了让它们独一无二?

Pau*_*tos 6

您可以利用 Swift泛型来实现您的目标。Index引入这样的泛型类型:

struct Index<T>: RawRepresentable {
    let rawValue: Int
    init(rawValue: Int) { self.rawValue = rawValue }
    init(_ rawValue: Int) { self.rawValue = rawValue }
}
Run Code Online (Sandbox Code Playgroud)

然后像这样使用它:

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

getHuman(at: Index(1))
getRobot(at: Index(2))
Run Code Online (Sandbox Code Playgroud)

文字索引

在使用文字索引时,您甚至可以使用ExpressibleByIntegerLiteral协议提供一些语法糖:

extension Index: ExpressibleByIntegerLiteral {
    public init(integerLiteral value: Int) { self.rawValue = value }
}
Run Code Online (Sandbox Code Playgroud)

例如:

getHuman(at: 1)
getRobot(at: 2)
Run Code Online (Sandbox Code Playgroud)

但以下代码将无法构建,因此该解决方案仍然是类型安全的:

let someIndex = 123
getHuman(at: someIndex)
Run Code Online (Sandbox Code Playgroud)

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
    }
}
Run Code Online (Sandbox Code Playgroud)

例子:

Index<Human>(1) < Index<Human>(2) // true
Run Code Online (Sandbox Code Playgroud)