1

I'm trying to create an extension on Set that uses a where clause so that it only works on a struct I have that accepts a generic. But I keep running into errors about it the extension wanting the generic to be defined in the struct

In this example I'm getting the following error, with the compiler hint suggesting I use <Any>: Reference to generic type 'Test' requires arguments in <...>

struct Test<T> {
    var value : T
    func printIt() {
        print("value")
    }
}

extension Set where Element == Test {
    
}

However, when I use <Any> in the struct, I'm getting this error: Same-type constraint type 'Test' does not conform to required protocol 'Equatable'

extension Set where Element == Test<Any> {
    
}

Any suggestions on how to get the where clause to accept the Test struct for any type I'm using in the generic?

Thanks for your help

slidmac07
  • 387
  • 1
  • 2
  • 14
  • The problem is that Test and Test are not one and the same type but two different types and the where clause requires _one_ specific type so Test alone will not suffice – Joakim Danielson Mar 13 '21 at 11:43
  • This isn't going to work. Conditional conformance requires a concrete type that the compiler can assess at compile time. – flanker Mar 13 '21 at 11:58

3 Answers3

1

This is a limitation of Swift's type system. There's no way to talk about generic types without concrete type parameters, even when those type parameters are unrelated to the use of the type. For this particular situation (an extension for all possible type parameters), I don't believe there's any deep problem stopping this. It's a simpler version of parameterized extensions, which is a desired feature. It's just not supported (though there is an implementation in progress).

The standard way to address this today is with a protocol.

First, a little cleanup that's not related to your question (and you probably already know). Your example requires Test to be Hashable:

struct Test<T: Hashable>: Hashable {
    var value : T
    func printIt() {
        print("value")
    }
}

Make a protocol that requires whatever pieces you want for the extension, and make Test conform:

protocol PrintItable {
    func printIt()
}

extension Test: PrintItable {}

And then use the protocol rather than the type:

extension Set where Element: PrintItable {
    func printAll() {
        for item in self { item.printIt() }
    }
}

let s: Set<Test<Int>> = [Test(value: 1)]

s.printAll()  // value

Just one more note on the error messages you're getting. The first error, asking you to add Any is really just complaining that Swift can't talk about unparameterized generics, and suggesting it's fallback type when it doesn't know what type to suggests: Any.

But Set<Any> isn't "any kind of Set." It's a Set where Element == Any. So Any has to be Hashable, which isn't possible. And a Set<Int> isn't a subtype of Set<Any>. There' completely different types. So the errors are a little confusing and take you down an unhelpful road.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610
0

This is not possible. The where clause requires a specific data type and simply passing a Test will not work unless I specify something more concrete like Test<String>.

Thank you to Joakim and flanker for answering the question in the comments

slidmac07
  • 387
  • 1
  • 2
  • 14
-2

If you want to add extension for Set with where clause your Test must confirm to Hashable protocol. Your Struct must look like this.

struct Test<T: Hashable> : Hashable {
    var value : T
    func printIt() {
        print("value")
    }
    func hash(into hasher: inout Hasher) {
        hasher.combine(value.hashValue)
    }
}

So you can't use Any for your extension you must specify type that confirm to Hashable protocol.