Le mystérieux "some" : pourquoi des milliers de développeurs s'interrogent-ils à son sujet ?
\n\nSi vous avez déjà écrit en SwiftUI, vous avez forcément vu la construction some View. Cela semble simple, mais les débutants sont souvent perplexes : "Qu'est-ce que ce 'some' ? Pourquoi ne puis-je pas simplement écrire View ? Et si je supprime some, ça va casser ?" La question "Que signifie le mot-clé 'some' en Swift(UI) ?" a recueilli des milliers de vues sur Stack Overflow — et pour cause. Derrière ce mot court se cache toute une philosophie du Swift moderne.
Dans cet article, nous allons comprendre ce qu'est le mot-clé some, pourquoi il est apparu dans le langage, et pourquoi SwiftUI est inimaginable sans lui. Vous apprendrez comment il se rapporte aux génériques, aux protocoles et aux "types opaques". Et surtout — vous comprendrez comment écrire un code plus propre et plus performant.
\n\nLe problème que résout "some"
\n\nImaginez que vous écrivez une fonction qui retourne un objet conforme au protocole View. En SwiftUI, cela arrive tout le temps :
func makeGreeting() -> View {\n return Text("Bonjour !")\n}\n\nLe compilateur Swift vous dira : "Le protocole 'View' ne peut être utilisé que comme une contrainte générique car il a des exigences Self ou de type associé." Cela semble effrayant, mais le principe est simple : le protocole View dans SwiftUI exige que le type soit connu au moment de la compilation. Si nous retournons View — c'est une abstraction, mais le compilateur a besoin de quelque chose de concret.
Auparavant, les développeurs contournaient cela avec des génériques :
\n\nfunc makeGreeting<T: View>() -> T {\n return Text("Bonjour !") as! T\n}\n\nCela fonctionnait, mais ressemblait à un bidouillage : vous promettez au compilateur de retourner T, mais en réalité vous "faites semblant". De plus, le code appelant devait spécifier explicitement le type. Moche et peu sûr.
"some" — Le sauveur du code passe-partout
\n\nLe mot-clé some en Swift est apparu dans la version 5.1 précisément pour ces cas. Il dit au compilateur : "Je vais retourner un type concret qui est conforme au protocole, mais je ne veux pas le nommer. Vous le déterminez au moment de la compilation." C'est ce qu'on appelle un type opaque.
Revenons à notre exemple :
\n\nfunc makeGreeting() -> some View {\n return Text("Bonjour !")\n}\n\nMaintenant, le code compile ! Le compilateur sait que c'est exactement Text qui est retourné, mais de l'extérieur nous ne voyons que some View. Cela nous apporte deux choses :
- \n
- Sécurité de type — le compilateur vérifie que le type de retour est toujours le même. \n
- Encapsulation — nous cachons les détails d'implémentation au code appelant. \n
Comparaison : "some" vs génériques vs protocoles
\n\nPour mieux comprendre la magie de some, comparons trois approches avec un exemple concret. Supposons que nous voulions écrire une fonction qui retourne une forme (un cercle ou un carré).
1. Retourner un protocole (ne compile pas)
\n\nprotocol Shape {\n func draw()\n}\n\nstruct Circle: Shape {\n func draw() { print("Dessiner un cercle") }\n}\nstruct Square: Shape {\n func draw() { print("Dessiner un carré") }\n}\n\n// Erreur : Le protocole 'Shape' ne peut pas être utilisé comme type de retour\nfunc makeShape() -> Shape {\n return Circle()\n}\n\nComme nous le savons déjà — cela ne fonctionne pas si le protocole a des types associés ou des exigences Self.
\n\n2. Utiliser les génériques
\n\nfunc makeShape<T: Shape>() -> T {\n // Besoin de créer T d'une manière ou d'une autre, mais nous ne connaissons pas le type concret\n fatalError("Impossible de créer un générique")\n}\n\nProblème : le code appelant doit savoir quel type attendre. La fonction ne peut pas décider elle-même quoi retourner.
\n\n3. Utiliser "some" (fonctionne parfaitement)
\n\nfunc makeShape() -> some Shape {