Impossible d'atsortingbuer une instance de class à son type de protocole?

S'il vous plaît voir self contenant l'exemple ci-dessous. Le compilateur signale une erreur sur la dernière ligne (marquée par COMPILE ERROR ) où COMPILE ERROR une instance de SimpleTrain à un type de protocole auquel (selon mon jugement) se conforme. Comment puis-je le faire comstackr? Qu'est-ce que je fais mal? Ou est ce problème de compilateur?

 protocol Train { typealias CarriageType func addCarriage(carriage: CarriageType) func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType } class SimpleTrain<T> : Train { typealias CarriageType = T private var carriages: [T] = [T]() func addCarriage(carriage: T) { carriages.append(carriage) } func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType { let short = SimpleTrain<T>() short.addCarriage(carriages[0]) return short //COMPILE ERROR: SimpleTrain<T> is not convertible to 'ShortType' } } 

EDIT: Même lorsque je shortTrain explicitement le type de return de shortTrain ci-dessus (de sorte que la dernière ligne de code ci-dessus lit return short as ShortType ) comme suggéré par Antonio il y a encore erreur de compilation lors de l'appel shortTrain function:

 let s = SimpleTrain<Ssortingng>() s.addCarriage("Carriage 1") s.addCarriage("Carriage 2") let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train' let b = s.shortTrain<SimpleTrain<Ssortingng>>() //ERROR: cannot explicitly specialize a generic function 

D'abord, vous voulez lire le fil canonique de devforums. Vous voulez particulièrement vous déplacer pour lire les commentaires de jckarter.

Maintenant à la question éditée:

 let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train' 

C'est parce que vous n'avez pas donné assez d'informations au compilateur pour déterminer le type de a . Pensez à ce qu'il voit:

 func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType { let a = s.shortTrain() 

Le compilateur doit comprendre le type de a au moment de la compilation, et il ne peut pas gérer les types abstraits. Il a besoin d'un type entièrement spécifié pour ShortType (tout ce qui est cloué, tous les generics spécifiés, tous les alias de type résolus). Il regarde autour de lui, et il voit des contraintes sur ShortType , mais il ne voit rien qui donne réellement un type. Tout ce qu'il a est a , qui ne lui donne aucun indice.

Donc, malheureusement, cela vous oblige à dire explicitement ce que vous voulez arriver.

 let a: SimpleTrain<Ssortingng> = s.shortTrain() 

C'est probablement le contraire de ce que vous recherchiez, mais c'est tout ce que vous pouvez faire dans Swift en ce moment. L'équipe Swift a indiqué (plusieurs fois) qu'elle est bien consciente de ces problèmes avec les types associés (et plusieurs autres faiblesses connexes dans le système de types). Ils sont particulièrement conscients du système de type Scala qui peut gérer ces choses et a beaucoup en commun avec le système de type Swift actuel (bien que d'après mon expérience, l'obtention de types complexes dépendants du pathement dans Scala peut également entraîner des cheveux déchirants ).

Cela dit, ce n'est pas exactement ce que vous avez prévu de faire avec cette fonctionnalité. Est-ce que certains Trains returnneraient un type différent comme shortTrain ()?

Je trouve que ces problèmes souvent exploser dans le cas général, mais ont tendance à être tout à fait soluble dans les cas spécifiques pour l'application en face de vous. Il est difficile de créer des types vraiment arbitraires dans Swift dans le code qui pourrait résoudre tous les problèmes, mais cela fonctionne souvent lorsque vous vous concentrez sur les types dont vous aurez vraiment besoin. Par exemple, si shortTrain() renvoie Self , cela devient évidemment plus simple. Si l'appelant connaît le type résultant souhaité, alors un init(shorten:) peut probablement le gérer. Une méthode de protocole comme shortCarriages() -> [CarriageType] peut fournir un bon pont. Restez flexible sur votre design, et l'un d'entre eux va presque certainement travailler.

Un downcast explicite de la variable:

 let short = SimpleTrain<T>() as ShortType 

ou la valeur de return:

 return short as ShortType 

résout le problème.

Mettre à jour

Quand je répondais à la question, je me suis demandé comment le protocole Train peut être utilisé comme type de return, étant typé.

Regardez ce code:

 protocol ProtocolWithNoAlias { } protocol ProtocolWithAlias { typealias AliasType } var x: ProtocolWithNoAlias? var y: ProtocolWithAlias? 

La dernière ligne est signalée comme une erreur de compilation:

 Protocol 'ProtocolWithAlias' can only be used as a generic constraint because it has Self os associated type requirements 

ce qui signifie que vous ne pouvez pas utiliser ProtocolWithAlias comme un type concret, ce qui signifie que vous ne pouvez pas déclarer des variables ayant le type ProtocolWithAlias , et par conséquent cela n'a pas de sens de définir la fonction qui le renvoie.

Je ne trouve aucune mention à ce sujet dans la documentation officielle, mais je suis sûr que j'ai lu quelque part, je ne me callbackle plus où.

Conclusion: j'interprète l'erreur que vous rencontrez:

 SimpleTrain<T> is not convertible to 'ShortType' 

comme une conséquence directe du protocole étant typealiased.

Notez que ceci est mon opinion personnelle , puisque je suis incapable en ce moment de le prouver en dehors de ce protocole de test de snippet de code avec et sans alias.

Avez-vous besoin d'utiliser un protocole? Je n'ai pas une très bonne compréhension de la théorie des types, mais le problème semble revenir au thème des «types élevés supérieurs», par la discussion sur le fil d'Apple et d'en haut.

Cela dit, si vous pouvez essayer d'approximer en utilisant une class de base abstraite (avec la mise en garde qui n'est pas réellement abstraite, et ne fonctionne pas pour les structures), avec le formulaire suivant:

 class AnyTrain<CarriageType> { func addCarriage(carriage: CarriageType) {} func shortTrain() -> AnyTrain<CarriageType> { return AnyTrain<CarriageType>() } } class SimpleTrain<T> : AnyTrain<T>, Printable { private var carriages: [T] = [T]() override func addCarriage(carriage: T) { carriages.append(carriage) } override func shortTrain() -> AnyTrain<T> { let short = SimpleTrain<T>() short.addCarriage(carriages[0]) return short //COMPILE ERROR: SimpleTrain<T> is not convertible to 'ShortType' } var description:Ssortingng { return "Train: \(carriages)" } } let train = SimpleTrain<Int>() train.addCarriage(1) train.addCarriage(2) train.addCarriage(3) let shortTrain = train.shortTrain() println(shortTrain) // obviously you can refer to it as the base let anotherTrain:AnyTrain<Int> = SimpleTrain<Int>() anotherTrain.addCarriage(3) anotherTrain.addCarriage(2) anotherTrain.addCarriage(1) let anotherShortTrain = anotherTrain.shortTrain() println(anotherShortTrain) 

Cela produit:

Train: [1]

Train: [3]

J'ai expérimenté avec cette technique pour les cas où j'ai des hiérarchies abstraites où un type est en commun, et l'autre diffère mais n'affecte pas les signatures de fonction. En utilisant une "class abstraite", je peux créer un tableau de "n'importe quel type avec ce type générique en commun (alors que les autres peuvent différer)"