Mulgard Mulgard - 2 months ago 8
Swift Question

How to use a Set in Swift 3.0 without generic usage

First of all i have to say i come from Java programming and compared with Java everything in Swift 3.0 seems to be totally complicated. I thought what i want to do is easy but it turned out it is not.

I have two objects:

protocol Customer {
}


and:

class Consulter {
}


I want my
Consulter
class to hold a
Set
of
Customer
:

class Consulter {
var customers: Set<Customer>;
}


Ok here the first thing. The Compiler now is complaining that
Customer
has to implement
Hashable
... really? Swift isnt doing that for me? Ok. So lets go for it:

func ==(lhs: Customer, rhs: Customer) -> Bool {
return lhs.hashValue == rhs.hashValue;
}

protocol Customer: Hashable {

var: hashValue: Int {
return "123".hashValue;
}
}


And in my
Consulter
class i now would have to do the following:

class Consulter<T: Customer> {

var customers: Set<T>;
}


Ok this is working. But now i have another class:

func ==(lhs: Location, rhs: Location) -> Bool { // here the error!
return lhs.hashValue == rhs.hashValue;
}

class Location<T: Customer> : Hashable {

var customer: T;

....
}


For the
Equatable
of the class
Location
i now get the error:

Reference to generic type 'Location' requires arguments in <...>


So what argument is the compiler expecting here? I dont know any concrete types at this point.

EDIT

my
Customer
protocol will later have different concrete implementations. A
Customer
can for example be a
Family
or a
Person
. In the
Consulter
class i want to have a
Set
of
Customer
containing both: families and persons. I think this is a simple and logical approach.

Answer

Since you intend to use types conforming to Customer in applications where they must be Hashable (e.g. as members of a Set), there is no reason why not to add this Hashable constraint directly to the Customer protocol. This way you move the responsibility to conformance to Hashable to the actual types that you consider to be Customer's

protocol Customer: Hashable {}

class Consulter<T: Customer> {
    var customers: Set<T>?
}

class Location<T: Customer>: Hashable {
    var customer: T
    init(customer: T) { self.customer = customer }

    var hashValue: Int {
        return customer.hashValue
    }
}

func ==<T: Customer>(lhs: Location<T>, rhs: Location<T>) -> Bool { 
    return lhs.customer == rhs.customer /* && ... test other properties */ 
}

Also, be careful using X.hashValue == Y.hashValue for testing for equality, since there is no guarantee that hashvalues are unique (consider them mainly used for clever "bin" categorization).

Or, since Swift 3

// ... as above

class Location<T: Customer>: Hashable {
    var customer: T
    init(customer: T) { self.customer = customer }

    var hashValue: Int {
        return customer.hashValue
    }

    static func ==(lhs: Location<T>, rhs: Location<T>) -> Bool { 
        return lhs.customer == rhs.customer /* && ... test other properties */ 
    }
}