Je suis mise en œuvre de ce qui suit:Comment contourner Swift ne supportant pas les méta-types de première classe?
- Un protocole
LanguageType
simple, qui est conforme àHashable
- Un protocole
Translateable
, qui devrait vous permettre d'obtenir (et mettre) un[String]
à partir d'un dictionnaire, en utilisant unLanguageType
comme la clé
// MARK: - LanguageType
protocol LanguageType: Hashable {
var description: String { get }
}
extension LanguageType {
var description: String { return "\(Self.self)" }
var hashValue: Int { return "\(Self.self)".hashValue }
}
func ==<T: LanguageType, U: LanguageType>(left: T, right: U) -> Bool {
return left.description == right.description
}
// MARK: - Translateable
protocol Translateable {
var translations: [LanguageType: [String]] { get set }
}
Comme d'habitude, Swift a un problème avec la façon dont le protocole est utilisé LanguageType
:
D'après ce que j'ai lu, cela a à voir avec Swift ne supportant pas Existentials, qui se traduit par des protocoles non étant en fait des types de première classe.
Dans le contexte des génériques, ce problème peut généralement être résolu avec une enveloppe effacée.
Dans mon cas, il n'y a pas de génériques ni de types associés.
Ce que je veux atteindre est d'avoir translations.Key
être toutLanguageType
, pas seulement un type générique conforme à LanguageType
.
Ainsi, par exemple cela ne fonctionnerait pas:
protocol Translateable {
typealias Language: LanguageType
var translations: [Language: [String]] { get set }
}
Pour une raison quelconque, je ne peux pas penser à un moyen d'y parvenir. Je trouve ça sonne comme je l'ai besoin d'une sorte d'emballage effacé type, comme je veux
translations.Key
pour être touteLanguageType
Je pense que je dois effacer le type exact, qui est censé pour se conformer à LanguageType
en Translateable
. Que puis-je faire pour résoudre ce problème?
Mise à jour 1: Comme on vient de déterminer dans this question, LanguageType
a fait des exigences de type associé (faire à sa conformité aux Equatable
). Par conséquent, je vais essayer de créer un wrapper de type effacé autour de LanguageType
.
Mise à jour 2: Je me suis rendu compte que la création d'une enveloppe effacée type pour LanguageType
résoudra pas vraiment le problème.J'ai créé AnyLanguage
:
struct AnyLanguage<T>: LanguageType {
private let _description: String
var description: String { return _description }
init<U: LanguageType>(_ language: U) { _description = language.description }
}
func ==<T, U>(left: AnyLanguage<T>, right: AnyLanguage<U>) -> Bool {
return left.description == right.description
}
Si je maintenant utilisé à la place de LanguageType
il ne ferait pas grand-chose, comme Translateable
aurait encore besoin d'un type associé:
protocol Translateable {
typealias T
var translations: [AnyLanguage<T>: [String]] { get set }
}
Solution:
J'ai retiré le générique de AnyLanguage
:
struct AnyLanguage: LanguageType {
private(set) var description: String
init<T: LanguageType>(_ language: T) { description = language.description }
}
func ==(left: AnyLanguage, right: AnyLanguage) -> Bool {
return left.description == right.description
}
protocol Translateable {
var translations: [AnyLanguage: [String]] { get set }
}
Vous ne savez pas pourquoi j'ai introduit T
dans la mise à jour 2, car il ne fait rien. Mais cela semble fonctionner maintenant.
Voulez-vous vraiment à mettre en œuvre l'égalité en comparant les valeurs de hachage? Cela ne marchera jamais. Le problème principal est que vous avez besoin de types concrets. Un dictionnaire ne peut pas fonctionner avec plusieurs classes qui ont différentes implémentations 'hash'. – Sulthan
Un type conforme à 'LanguageType' n'est jamais censé implémenter sa propre version de' hashValue'. Donc 'hashValue' est seulement déclaré comme une extension. L'égalité serait donc basée uniquement sur les noms des langues, ce qui me semble assez bon. –
L'égalité ne peut pas être basée sur des fonctions de hachage. Vous devez toujours comparer les chaînes, pas seulement leurs hachages. – Sulthan