Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to do type-safe indices in Swift?

I'm trying to do something like this:

typealias HumanId = Int 
typealias RobotId = Int

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

but as it is now I can call getHuman with RobotId just fine: getHuman(at: RobotId(0)).

How do I make this typesafe?


I understand that I can do something like:

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

...and some extra things to make these structs function as indices, but that would lead to some code duplication, and since I'm having more than 2 of these id-types I would like to shorten this somehow, with typealiases and generics perhaps in order to make them unique?

like image 825
Paulius Liekis Avatar asked Oct 14 '25 08:10

Paulius Liekis


1 Answers

You could leverage Swift generics to achieve your goal. Introduce a generic Index type like this:

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

and then use it like this:

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

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

Literal Indices

You could even use the ExpressibleByIntegerLiteral protocol to provide some syntax sugar when using literal indices:

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

For instance:

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

But the following code will not build, so the solution is still typesafe-ish:

let someIndex = 123
getHuman(at: someIndex)

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

Comparable Indices

As suggested in the comments, we could also add Comparable conformance as well (e.g., so you can use the Index struct as the index in a type conforming to the standard Collection protocol):

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

Example:

Index<Human>(1) < Index<Human>(2) // true
like image 76
Paulo Mattos Avatar answered Oct 18 '25 06:10

Paulo Mattos