I subscripts in Swift offrono una sintassi conveniente per accedere agli elementi di una collezione, contenitore o tipo personalizzato tramite indici, proprio come negli array e nei dizionari. Possono essere dichiarati e sovrascritti nelle proprie classi, strutture e enumerazioni.
I subscripts sono stati introdotti in Swift come un modo per unificare l'accesso indicizzato agli oggetti, iniziando dalle collezioni e poi espandendosi a qualsiasi tipo personalizzato. L'obiettivo principale è aumentare l'espressività e la leggibilità, trasformare i metodi di accesso ai dati in una sintassi "naturale" che ricorda il lavoro con un array.
In altri linguaggi (ad esempio, Java), l'accesso a un elemento di una struttura avviene tramite metodi get/set, il che rende il codice ingombrante. Il desiderio di una migliore leggibilità e di facilitare l'accesso ai dati ha portato all'istituzione dei subscripts. In Swift è necessaria particolare attenzione all'accesso sicuro, ai getter e ai setter, agli errori potenziali e ai valori di ritorno optional.
I subscripts possono essere dichiarati in qualsiasi classe, struttura o enumerazione utilizzando la parola chiave subscript. Si supportano anche più overload in base ai parametri e ai tipi di ritorno.
Esempio di subscript personalizzato:
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
Caratteristiche chiave:
È possibile dichiarare un subscript solo con il getter, senza setter?
Sì. Se il subscript è necessario solo per la lettura, è sufficiente implementare solo il get.
struct ReadOnlyArray { private let items = [1, 2, 3] subscript(index: Int) -> Int { get { return items[index] } } }
È consentito dichiarare più subscripts con diverse firme nello stesso tipo?
Sì, i subscripts possono essere sovraccaricati (overloaded) in base al numero e al tipo di parametri.
struct Collection { subscript(index: Int) -> String { ... } subscript(key: String) -> Int? { ... } }
Può un subscript essere dichiarato in un protocollo e implementato in una struttura/classe?
Sì, i protocolli possono richiedere l'implementazione di subscripts, e i tipi specifici sono obbligati a implementarli.
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 sviluppatore ha implementato un subscript senza controlli sui limiti. Di conseguenza, l'applicazione crashava con un errore durante l'esecuzione quando si tentava di accedere a un indice inesistente.
Pro: Il codice è diventato più conciso.
Contro: L'applicazione ha cominciato a chiudersi inaspettatamente a causa di errori improvvisi.
Implementazione di un subscript con precondition e possibilità di ritorno di optional o lancio di errori.
Pro: Il codice è diventato più sicuro per l'utente, gli errori possono essere gestiti.
Contro: È necessaria un'ulteriore logica per la gestione degli errori e considerare il comportamento per indici non validi.