Les subscripts en Swift offrent une syntaxe pratique pour accéder aux éléments d'une collection, d'un conteneur ou d'un type utilisateur à l'aide d'indices, comme dans les tableaux et les dictionnaires. Ils peuvent être déclarés et redéfinis dans vos propres classes, structures et énumérations.
Les subscripts sont apparus en Swift comme un moyen de normaliser l'accès indexé aux objets, en commençant par les collections, puis en s'étendant à tous les types utilisateurs. L'objectif clé est d'améliorer l'expressivité et la lisibilité, transformant les méthodes d'accès aux données en une syntaxe "naturelle" qui ressemble au travail avec un tableau.
Dans d'autres langages (par exemple, Java), l'accès à un élément d'une structure se fait par des méthodes get/set, ce qui rend le code alambiqué. Le désir d'une meilleure lisibilité et d'un accès facilité aux données a engendré les subscripts. En Swift, une attention particulière est requise pour un accès sûr, un getter et un setter, les erreurs potentielles et les valeurs de retour optionnelles.
Un subscript peut être déclaré dans n'importe quelle classe, structure ou énumération à l'aide du mot-clé subscript. De plus, plusieurs surcharge par paramètres et types de retour sont prises en charge.
Exemple de subscript utilisateur :
struct Matrix { let rows: Int, columns: Int private var grid: [Double] init(rows: Int, columns: Int) { self.rows = rows self.columns = columns grid = Array(repeating: 0.0, count: rows * columns) } subscript(row: Int, column: Int) -> Double { get { precondition(row >= 0 && row < rows) precondition(column >= 0 && column < columns) return grid[(row * columns) + column] } set { precondition(row >= 0 && row < rows) precondition(column >= 0 && column < columns) grid[(row * columns) + column] = newValue } } } var matrix = Matrix(rows: 2, columns: 2) matrix[1, 1] = 3.14 print(matrix[1, 1]) // 3.14
Caractéristiques clés :
Peut-on déclarer un subscript seulement avec un getter, sans setter ?
Oui. Si le subscript est uniquement nécessaire pour la lecture, il suffit de mettre en œuvre uniquement get.
struct ReadOnlyArray { private let items = [1, 2, 3] subscript(index: Int) -> Int { get { return items[index] } } }
Est-il permis de déclarer plusieurs subscripts avec différentes signatures dans un même type ?
Oui, les subscripts peuvent être surchargés (overload) par le nombre et le type de paramètres.
struct Collection { subscript(index: Int) -> String { ... } subscript(key: String) -> Int? { ... } }
Un subscript peut-il être déclaré dans un protocole et implémenté dans une structure/classe ?
Oui, les protocoles peuvent exiger l'implémentation de subscripts, et les types spécifiques doivent les implémenter.
protocol MySubscriptable { subscript(index: Int) -> String { get set } } struct SubData: MySubscriptable { private var items = ["a", "b"] subscript(index: Int) -> String { get { return items[index] } set { items[index] = newValue } } }
Un développeur a implémenté un subscript sans vérification des limites. En conséquence, lorsque l'on accède à un indice inexistant, l'application plante avec une erreur à l'exécution.
Avantages : Le code est devenu plus concis.
Inconvénients : L'application a commencé à planter en raison d'erreurs imprévues.
Mise en œuvre d'un subscript avec des preconditions et la possibilité de retourner optionnels ou de lever des erreurs.
Avantages : Le code est devenu plus sûr pour l'utilisateur, les erreurs peuvent être gérées.
Inconvénients : Besoin d'intégrer une logique supplémentaire pour la gestion des erreurs et de prévoir le comportement pour des indices non valides.