some View
es un tipo de resultado opaco introducido por SE-0244 y está disponible en Swift 5.1 con Xcode 11. Puede pensar en esto como un marcador de posición genérico "inverso".
A diferencia de un marcador de posición genérico regular que satisface la persona que llama:
protocol P {}
struct S1 : P {}
struct S2 : P {}
func foo<T : P>(_ x: T) {}
foo(S1()) // Caller chooses T == S1.
foo(S2()) // Caller chooses T == S2.
Un tipo de resultado opaco es un marcador de posición genérico implícito satisfecho por la implementación , por lo que puede pensar en esto:
func bar() -> some P {
return S1() // Implementation chooses S1 for the opaque result.
}
como se ve así:
func bar() -> <Output : P> Output {
return S1() // Implementation chooses Output == S1.
}
De hecho, el objetivo final con esta función es permitir genéricos inversos en esta forma más explícita, lo que también le permitiría agregar restricciones, por ejemplo -> <T : Collection> T where T.Element == Int
. Vea esta publicación para más información .
Lo principal que debemos sacar de esto es que una función que devuelve some P
es aquella que devuelve un valor de un tipo concreto único específico que se ajusta a P
. Intentar devolver diferentes tipos conformes dentro de la función produce un error de compilación:
// error: Function declares an opaque return type, but the return
// statements in its body do not have matching underlying types.
func bar(_ x: Int) -> some P {
if x > 10 {
return S1()
} else {
return S2()
}
}
Como el marcador de posición genérico implícito no puede ser satisfecho por múltiples tipos.
Esto contrasta con el retorno de una función P
, que se puede usar para representar ambos S1
y S2
porque representa un P
valor conforme arbitrario :
func baz(_ x: Int) -> P {
if x > 10 {
return S1()
} else {
return S2()
}
}
Bien, entonces, ¿qué beneficios tienen los tipos de resultados opacos -> some P
sobre los tipos de retorno de protocolo -> P
?
1. Los tipos de resultados opacos se pueden usar con PAT
Una limitación importante actual de los protocolos es que los PAT (protocolos con tipos asociados) no pueden usarse como tipos reales. Aunque esta es una restricción que probablemente se eliminará en una versión futura del lenguaje, debido a que los tipos de resultados opacos son efectivamente marcadores de posición genéricos, hoy en día se pueden usar con PAT.
Esto significa que puede hacer cosas como:
func giveMeACollection() -> some Collection {
return [1, 2, 3]
}
let collection = giveMeACollection()
print(collection.count) // 3
2. Los tipos de resultados opacos tienen identidad
Debido a que los tipos de resultados opacos obligan a que se devuelva un solo tipo concreto, el compilador sabe que dos llamadas a la misma función deben devolver dos valores del mismo tipo.
Esto significa que puede hacer cosas como:
// foo() -> <Output : Equatable> Output {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
let x = foo()
let y = foo()
print(x == y) // Legal both x and y have the return type of foo.
Esto es legal porque el compilador sabe que ambos x
y y
tienen el mismo tipo concreto. Este es un requisito importante para ==
, donde ambos parámetros de tipo Self
.
protocol Equatable {
static func == (lhs: Self, rhs: Self) -> Bool
}
Esto significa que espera dos valores que sean del mismo tipo que el tipo de conformación concreta. Incluso si Equatable
fuera utilizable como tipo, no podría comparar dos Equatable
valores conformes arbitrarios entre sí, por ejemplo:
func foo(_ x: Int) -> Equatable { // Assume this is legal.
if x > 10 {
return 0
} else {
return "hello world"
}
}
let x = foo(20)
let y = foo(5)
print(x == y) // Illegal.
Como el compilador no puede probar que dos Equatable
valores arbitrarios tienen el mismo tipo concreto subyacente.
De manera similar, si presentamos otra función de retorno de tipo opaco:
// foo() -> <Output1 : Equatable> Output1 {
func foo() -> some Equatable {
return 5 // The opaque result type is inferred to be Int.
}
// bar() -> <Output2 : Equatable> Output2 {
func bar() -> some Equatable {
return "" // The opaque result type is inferred to be String.
}
let x = foo()
let y = bar()
print(x == y) // Illegal, the return type of foo != return type of bar.
El ejemplo se vuelve ilegal porque aunque ambos foo
y bar
devuelven some Equatable
, sus marcadores de posición genéricos "inversos" Output1
y Output2
podrían ser satisfechos por diferentes tipos.
3. Los tipos de resultados opacos se componen con marcadores genéricos
A diferencia de los valores normales de tipo protocolo, los tipos de resultados opacos se componen bien con marcadores de posición genéricos regulares, por ejemplo:
protocol P {
var i: Int { get }
}
struct S : P {
var i: Int
}
func makeP() -> some P { // Opaque result type inferred to be S.
return S(i: .random(in: 0 ..< 10))
}
func bar<T : P>(_ x: T, _ y: T) -> T {
return x.i < y.i ? x : y
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Legal, T is inferred to be the return type of makeP.
Esto no habría funcionado si makeP
hubiera regresado P
, ya que dos P
valores pueden tener diferentes tipos de hormigón subyacentes, por ejemplo:
struct T : P {
var i: Int
}
func makeP() -> P {
if .random() { // 50:50 chance of picking each branch.
return S(i: 0)
} else {
return T(i: 1)
}
}
let p1 = makeP()
let p2 = makeP()
print(bar(p1, p2)) // Illegal.
¿Por qué usar un tipo de resultado opaco sobre el tipo concreto?
En este punto, puede estar pensando para usted mismo, ¿por qué no simplemente escribir el código como:
func makeP() -> S {
return S(i: 0)
}
Bueno, el uso de un tipo de resultado opaco le permite convertir el tipo en S
un detalle de implementación al exponer solo la interfaz proporcionada por P
, dándole flexibilidad para cambiar el tipo concreto más adelante en la línea sin romper ningún código que dependa de la función.
Por ejemplo, podría reemplazar:
func makeP() -> some P {
return S(i: 0)
}
con:
func makeP() -> some P {
return T(i: 1)
}
sin romper ningún código que llame makeP()
.
Consulte la sección Tipos opacos de la guía de idiomas y la propuesta de evolución rápida para obtener más información sobre esta función.