Un Opaque Type in Swift, anche conosciuto come Opaque Return Type, è un concetto introdotto con Swift 5.1. Semplifica l’uso di Protocol e Generic, soprattutto con Protocol che hanno Associated Type. Questo articolo spiega cosa sono gli Opaque Type e come usarli.
Problemi con Protocol e Generic
Quando una funzione restituisce un Protocol con Associated Type, è difficile definire il tipo di dato specifico per il valore di ritorno. Esempio:
protocol P {
associatedtype Value
var value: Value { get }
init(value: Value)
}
struct S1 : P {
var value: Int
}
struct S2: P {
var value: String
}
// Errore: Impossibile dedurre il tipo di ritorno
// func foo() -> P { return S1() }
Anche l’uso dei Generic ha dei limiti perché è necessario specificare il tipo di dato per il parametro o il valore di ritorno della funzione:
func foo<T: P>(value: T.Value) -> T { return T(value: value) }
let s1: S1 = foo(value: 10)
let s2: S2 = foo(value: "ahihi")
Soluzione con gli Opaque Type
Un Opaque Type, dichiarato con la parola chiave some
, permette alla funzione di restituire un tipo di dato “nascosto” che deve solo conformarsi al Protocol, senza bisogno di specificarlo.
func foo() -> some P { return S1(value: 11) }
Nell’esempio sopra, some P
rappresenta un tipo di dato specifico che soddisfa il Protocol P
; il compilatore lo deduce automaticamente.
Tuttavia, un Opaque Type permette di restituire un solo tipo di dato nella stessa funzione:
// Errore: Impossibile restituire tipi di dato diversi
// func bar(_ x: Int) -> some P {
// if x > 10 { return S1(value: 12) }
// else { return S2(value: "ahihi") }
// }
Opaque Type e PAT (Protocol with Associated Types)
Gli Opaque Type sono particolarmente utili quando si lavora con i PAT. Permettono di usare i PAT come tipo di ritorno senza conoscere il tipo di dato specifico, rendendo il codice più flessibile e non vincolato alla versione della libreria.
func giveMeACollection() -> some Collection { return [1, 2, 3] }
let collection = giveMeACollection()
print(collection.count) // 3
Opaque Type e Tipo Definito
Quando una funzione con Opaque Type restituisce un tipo specifico, il compilatore garantisce che le chiamate a quella funzione restituiscano sempre lo stesso tipo.
func fooo() -> some Equatable { return 5 }
let f1 = fooo()
let f2 = fooo()
print(f1 == f2) // true
Tuttavia, questo non vale per i PAT. Ogni funzione con Opaque Type e PAT restituirà un tipo di dato distinto, anche se entrambi si conformano allo stesso Protocol.
Opaque Type e Generic Placeholders
Combinare Opaque Type con Generic Placeholders permette di creare funzioni versatili con diversi tipi di dato, senza dover restituire un tipo specifico.
Perché usare gli Opaque Type?
Gli Opaque Type offrono flessibilità quando si lavora con i Protocol, in particolare con i PAT. Evitano di dover definire il tipo di dato specifico, riducono il vincolo alla versione della libreria e aumentano la riusabilità del codice.
Opaque Type in SwiftUI
Gli Opaque Type sono ampiamente utilizzati in SwiftUI. La proprietà body
del View Protocol ha il tipo some View
, permettendo di restituire qualsiasi tipo di dato che si conforma al View Protocol, semplificando la creazione dell’interfaccia utente. L’uso di some View
evita tipi di dato complessi quando una View ha più livelli annidati.