I'm trying to implement following structure using generics. Getting a weird compiler error, can't figure out why.
class Translator<T:Hashable> {...}
class FooTranslator<String>:Translator<String> {...}
The idea is that Translator uses T as the type of a key in a dictionary. This can be e.g. a String or an enum. Subclass provides concrete dictionary.
But it fails because of this: "Type 'String' does not conform to protocol 'Hashable'"
But String conforms to Hashable! Am I nuts? It also doesn't work with Int, which also conforms to Hashable. It also doesn't work if I replace Hashable with Equatable, which should be also implemented by both.
If I remove the type constraint, just for testing (where I also have to disable the dictionary, as I can't use anything not hashable as key there) - it compiles
class Translator<T> {...}
class FooTranslator<String>:Translator<String> {...}
What am I doing wrong?
I'm not a Swift developer, but having seen similar problems in Java, I suspect the problem is that at the moment you're declaring a type parameter called String
because you're declaring class FooTranslator<String>
- so the type argument in Translator<String>
is just that type parameter, which has no constraints. You don't want a type parameter at all, I suspect (i.e. you don't want your FooTranslator
to be a generic class itself.)
As noted in comments, in Swift subclasses of a generic class also have to be generic. You could possibly declare a throw-away type parameter, like this:
class FooTranslator<T>:Translator<String>
which still avoids declaring a new type parameter called String
, which was what was causing the problem. It means you're introducing a new type parameter when you don't want any type parameters, but it's possibly better than nothing...
This is all on the assumption that you really need a subclass, e.g. to add or override members. On the other hand, if you just want a type which is exactly the same as Translator<String>
, you should use a type alias instead:
typealias FooTranslator = Translator<String>
Or even mix the two in a horrible way, if you genuinely want a subclass but don't want to have to refer to it in a generic way:
class GenericFooTranslator<T>:Translator<String>
typealias FooTranslator = GenericFooTranslator<Int>
(Note that the Int
here is deliberately not String
, to show that the T
in Translator
isn't the same as the T
in FooTranslator
.)
See more on this question at Stackoverflow