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

How to do type-safe indices in Swift?

提问人:Paulius Liekis 提问时间:5/15/2023 最后编辑:Paulo MattosPaulius Liekis 更新时间:5/19/2023 访问量:103

问:

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

typealias HumanId = Int 
typealias RobotId = Int

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

但就像现在一样,我可以打电话就好了:.getHumanRobotIdgetHuman(at: RobotId(0))

如何使这种类型安全?


我知道我可以做这样的事情:

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

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

Swift 泛型 集合 type-safety type-alias

评论


答:

6赞 Paulo Mattos 5/15/2023 #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>'

可比指数

正如注释中建议的那样,我们还可以添加一致性(例如,您可以将结构用作符合标准协议的类型的索引):ComparableIndexCollection

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

例:

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

评论

1赞 JeremyP 5/15/2023
我喜欢这个答案的优雅。应添加一致性,以便可以将该类型用作符合以下条件的类型的索引ComparableCollection