How to factor out common sorting implementation?

I have a number of structs which all have a common 'order' property, and I want to make a protocol to avoid repeating the func < implementation everywhere.

Unfortunately, the obvious thing doesn't work:

protocol Orderable: Comparable {
    var order: Int { get }
}

extension Orderable {
    static func < (lhs: any Orderable, rhs: any Orderable) -> Bool {
        return lhs.order < rhs.order
    }
}

struct Item: Decodable, Orderable {
    let name: String
    let order: Int
}

What is the Swift way to express this?

1 Like

Comparable.< requires its parameters to be of the type Self, so try this instead:

extension Orderable {
  static func < (lhs: Self, rhs: Self) -> Bool { ... }
}
2 Likes
protocol Orderable: Comparable {
  var order: Int { get }
}

extension Orderable {
  static func < <T: Orderable>(lhs: Self, rhs: T) -> Bool {
    return lhs.order < rhs.order
  }
}

This one might work if you expect that the lhs and rhs might be different types.

Some sideways advice is that an Array<any Orderable> can sometimes be looked at from a different POV. Instead of a hetergenous collection of struct elements… could you build a homogenous collection of AGT enum elements?

1 Like

Ah thanks! That was enough to make it work.

import Foundation

protocol Orderable: Comparable {
    var order: Int { get }
}

extension Orderable {
    static func < (lhs: Self, rhs: Self) -> Bool {
        return lhs.order < rhs.order
    }
}

struct Item: Decodable, Orderable {
    let name: String
    let order: Int
}

print([Item(name: "a", order: 2), Item(name: "b", order: 1)].sorted())
1 Like

Ah interesting, that works as well:

import Foundation

protocol Orderable: Comparable {
    var order: Int { get }
}

extension Orderable {
    static func < <T: Orderable>(lhs: Self, rhs: T) -> Bool {
        return lhs.order < rhs.order
    }
}

struct Item: Decodable, Orderable {
    let name: String
    let order: Int
}

struct Thing: Decodable, Orderable {
    let name: String
    let order: Int
}

print(Item(name: "a", order: 2) < Thing(name: "b", order: 1))