-2

Suppose I have this:

protocol MyStuff: Hashable {
    var stuff: String { get }
}
extension MyStuff {
    func hash(into hasher: inout Hasher) {
        hasher.combine(stuff)
    }
    static func == (lhs: Self, rhs: Self) -> Bool {
        return lhs.stuff == rhs.stuff
    }
}
struct Stuff: MyStuff {
    let stuff: String
}

Where I have a protocol which conforms to Hashable, and I extend that protocol to implement the requirements of Hashable. This works great.

I'm now trying to use the api like this:

let set: Set<MyStuff> = [Stuff(stuff: "Stuff")]

However I get this error:

Protocol 'MyStuff' as a type cannot conform to 'Hashable'

This question is actually a follow up to this answer written by George, and in that answer it says that

I believe SE-0309 (Unlock existentials for all protocols) could fix this.

I was wondering if that (the above link) would actually apply into this situation (listed in the code block above), as I'm having trouble fully understanding the proposal.

Cristik
  • 30,989
  • 25
  • 91
  • 127
  • 2
    Found a similar question [here](https://stackoverflow.com/q/64468530/9607863), in which the answer uses type erasure. Might be interesting to have a look at that and see if that works in your case. Also I do need to try understand SE-0309 a little bit myself too :p – George Aug 31 '21 at 01:15
  • What you need is a set of Stuff not iStuff – Leo Dabus Aug 31 '21 at 01:19
  • @George Thanks for the question reference! I'll look into that –  Aug 31 '21 at 01:20
  • @LeoDabus Unfortunately, for my case I'm trying to use Set operations on 2 different types which both conform to the same protocol –  Aug 31 '21 at 01:20
  • Self is not generic it can not be two different types – Leo Dabus Aug 31 '21 at 01:21
  • @LeoDabus However both types have the same == and hash functions in the extension, so I was wondering if the linked proposal will enable this –  Aug 31 '21 at 01:23
  • 1
    It doesnt matter. A protocol can not conform to another protocol – Leo Dabus Aug 31 '21 at 01:23
  • 1
    I don't think the proposal mentioned will affect that behavior – Leo Dabus Aug 31 '21 at 01:29
  • @Joe The behavior you are looking for is more similar to a class. Just create a class Stuff that conforms to Hashable. Then you can subclass it creating Stuff1, Stuff2 and also have a set of Stuff – Leo Dabus Aug 31 '21 at 01:40
  • @LeoDabus That's actually my current solution, but due to other reasons I have to switch to struct –  Aug 31 '21 at 01:43
  • Seems to me like we should close this, as “will this proposal potentially solve my problem at an indeterminate time in the future” isn’t appropriate for Stack Overflow. Any reason to keep it open, given that, Joe? (In the meantime, you just need type erasure.) (Also, putting an `I` in front is not Swift. That’s Java or C#.) –  Aug 31 '21 at 04:40

1 Answers1

1

There are a couple of issues here, not necessarily due to the code you wrote, but due to how Swift is currently designed:

  1. Protocols don't conform to other protocols, which means that MyStuff is not a sub-type of Hashable, which means you cannot use it as argument for the generic Set
  2. Protocols with associated types, or self requirements, like Equatable, from which Hashable derives, can't be used as generic arguments, can only be used as generic constraints

The compiler runs into issue #1 from above, and that one gives the (clear maybe) message that protocols as types cannot be used as generic arguments instead of other protocols.

SE-0309 might solve issue #2, however you're stuck on #1, so you'll have to change your design.

Solutions? As others have suggested in the comments:

  1. use a type eraser (e.g. https://stackoverflow.com/a/64476569/1974224)
  2. use classes, and replace the protocol by a base class
Cristik
  • 30,989
  • 25
  • 91
  • 127