Search code examples
swiftgenericsequality

How do I define equality for a generic Result<T> type?


I have a plain Result type:

public enum Result<T> {
    case success(T)
    case error
}

I want to make the type Equatable, easy enough:

public enum Result<T: Equatable>: Equatable {

    case success(T)
    case error

    // definition of ==
}

But then I want to use Result<Void>, and that’s a type error since Void doesn’t conform to Equatable. Is there a way to define a Result type that would conform to Equatable, accept Result<Void> and still use the correct equality check for T: Equatable? Wouldn’t it make sense for Void to implement Equatable?


Solution

  • I don't think that is possible at present. Void is the type of the empty tuple (), and tuples cannot adopt protocols (a discussion about that topic starts at [swift-evolution] Synthesizing Equatable, Hashable, and Comparable for tuple types).

    A possible workaround (as suggested by @Hamish above) is to use a custom type instead of Void:

    struct Unit: Equatable {
        static var unit = Unit()
        public static func ==(lhs: Unit, rhs: Unit) -> Bool {
            return true
        }
    }
    
    let res = Result.success(Unit.unit)
    

    I initially though that once SE-0143 Conditional conformances is implemented then one could define

    public enum Result<T> {
        case success(T)
        case error
    }
    
    public extension Result: Equatable where T: Equatable {
        public static func ==(lhs: Result, rhs: Result) -> Bool {
            // ...
        }
    }
    
    public extension Result: Equatable where T == Void {
        public static func ==(lhs: Result, rhs: Result) -> Bool {
            return true
        }
    }
    

    without the need the make Void itself Equatable.

    However (again attribution goes to @Hamish) this won't work because multiple conformances won't be permitted.