swift - edad - ellie goulding novio
¿Qué es el tipo Never return? (4)
¿Qué hace una func
con tipo de retorno Never
hacer?
Por ejemplo:
func addNums() -> Never {
//my code
}
¿Cuál será la diferencia si mantengo el tipo de devolución como Void
esta manera?
func addNums() -> Void {
//my code
}
Supongamos que deseo manejar un fatalError
(como dice dpassage ); El siguiente código será suficiente:
print("its an error")
return
La documentación de Apple dice:
El tipo de retorno de funciones que no devuelven normalmente, es decir, un tipo sin valores.
Fuente: Developer
Esta no fue una pregunta duplicada de ¿ Cuándo y cómo usar el atributo @noreturn en Swift? , como deseo una respuesta más detallada que necesita detalles como:
Ejemplos prácticos sobre la diferencia entre los tipos
Never
yVoid
como retorno.Condición por la cual debemos adoptar estos tipos de devolución.
También existe la posibilidad de que el tipo de retorno sea nulo; Necesito una comparación de esa característica también
La respuesta debe centrarse en las diferencias.
Para comprender mejor Never
and Void
, y cómo Never
es útil en más contextos que el antiguo @noreturn
, primero veamos cómo se definen realmente los dos tipos como:
Never
se define here como:
public enum Never {}
Como no hay manera de crear una instancia de un valor de una enumeración vacía, el sistema de tipos garantiza que no puede existir ninguna instancia de Never
. Esto significa que las funciones que especifican su tipo de devolución como Never
se evitan por el sistema de tipos en cualquier circunstancia.
El compilador tiene esto en cuenta cuando hace un análisis de flujo de control. Por ejemplo, estas dos funciones se compilan sin error, mientras que fallarían si una función que devuelve Void
se sustituya por fatalError
:
func foo(fail: Bool) -> String {
if fail {
fatalError()
} else {
return "foo"
}
// notice there is no return statement here
}
func bar(fail: Bool) -> Void {
let s: String
if fail {
fatalError()
// the compiler doesn''t complain s is not initialized here
} else {
s = "bar"
}
print(s)
}
Void
se define here como:
public typealias Void = ()
No hay dos instancias diferentes de una tupla vacía. Por lo tanto, el valor de retorno de las funciones que devuelven Void
no contiene información.
Realmente puedes escribir return ()
o return Void()
. También puedes usar el "valor" devuelto, así:
func empty() -> Void {}
let v = empty()
print(type(of: v)) // prints "()"
aunque el compilador advertirá que "Constante ''v'' tiene el tipo ''Vacío'', lo que puede ser inesperado".
Definir ambos, Never
y Void
en términos del sistema de tipos en lugar de como características especiales del lenguaje, nos permite hacer cosas muy inteligentes con los genéricos. Veamos un ejemplo de un tipo de Result
, genérico sobre el tipo de éxito y fracaso.
enum Result<R, E> {
case success(R)
case failure(E)
}
Una posible especialización de esto sería Result<Void, MyError>
. Esto significaría que tiene un resultado que, en caso de éxito, no contiene ninguna información más allá del hecho de que tuvo éxito.
Otra posibilidad podría ser Result<String, Never>
. El compilador garantiza que este resultado nunca será el caso de falla.
Los opcionales interactúan con Never
y el Void
de manera similar. Never?
¿Solo puede ser nulo, y Void?
solo guarda la información si es nula o no, nada más (es básicamente un Bool más complicado). Ambos de estos no son muy útiles por sí solos, pero pueden aparecer cuando Never
o Void
se utilizan como parámetros genéricos en algún lugar.
En la práctica, raramente escribirá funciones que vuelven Never
. Personalmente lo he usado para envolver fatalError
para crear una función que uso para marcar funciones que aún no están implementadas:
func unimplemented(f: String = #function) -> Never {
fatalError("/(f) is not implemented yet")
}
Otro ejemplo de una función que devuelve Never
es dispatchMain()
, que se puede usar en las utilidades de línea de comandos para iniciar DispatchQueue.main
. Como esta cola espera nuevos bloques, dispatchMain()
nunca regresa.
Never
indica que la función nunca volverá. Está diseñado para ser utilizado para cosas como fatalError
que causan que su programa se bloquee intencionalmente, a menudo después de registrar un error. Probablemente no debería usarlo a menos que esté haciendo algo como hacer un controlador para errores catastróficos en su aplicación.
Esto es diferente de una función que simplemente no devuelve un valor, como en su segundo fragmento. También puede escribir eso como func addNums() -> Void
.
Never
se introdujo el tipo devuelto en Swift 3 para sustituir la clave @noreturn
.
Ver justificación en esta propuesta:
SE-0102 Elimine el atributo @noreturn e introduzca un tipo vacío Nunca
Como explica la documentación oficial:
El tipo de retorno de funciones que no retornan normalmente; Un tipo sin valores.
Utilice Nunca como el tipo de retorno al declarar un cierre, función o método que incondicionalmente lanza un error, atrapa o no termina.
Fuente: Developer
Ilustración básica:
// The following function is our custom function we would use
// to manually and purposefully trigger crash. In the logs,
// we can specify what exactly went wrong: e.g. couldn''t cast something,
// couldn''t call something or some value doesn''t exist:
func crashApp() -> Never {
fatalError("Something very, very bad happened! Crash the app!")
}
Los detalles y ventajas de uso sobre @noreturn
, según lo referenciado por Erica Sadun :
- Nunca permite que una función o método lance: por ejemplo () lanzamientos -> Nunca. El lanzamiento permite una ruta secundaria para la corrección de errores, incluso en funciones que no se esperaba que regresaran.
- Como un tipo de primera clase, Nunca funciona con genéricos de una manera que el atributo @noreturn no podría.
- Nunca evite proactivamente que una función reclame tanto un tipo de devolución como una no devolución al mismo tiempo. Este era un problema potencial bajo el sistema antiguo.
La primera nota (con respecto a la corrección de errores secundarios) es probablemente particularmente importante. Never
función puede tener una lógica compleja y un lanzamiento, no necesariamente falla.
Veamos algunos casos de uso interesantes y comparación entre Never
y Void
Nunca
Ejemplo 1
func noReturn() -> Never {
fatalError() // fatalError also returns Never, so no need to `return`
}
func pickPositiveNumber(below limit: Int) -> Int {
guard limit >= 1 else {
noReturn()
// No need to exit guarded scope after noReturn
}
return rand(limit)
}
Ejemplo 2
func foo() {
abort()
print("Should not reach here") // Warning for this line
}
Ejemplo 3
func bar() -> Int {
if true {
abort() // No warning and no compiler error, because abort() terminates it.
} else {
return 1
}
}
abort()
se define como:
public func abort() -> Never
Vacío
Estos ejemplos no habrían sido posibles con la devolución de Void
:
public func abortVoid() -> Void {
fatalError()
}
func bar() -> Int {
if true {
abortVoid() // ERROR: Missing return in a function expected to return ''Int''
} else {
return 1
}
}
Y para empacar con abort()
regresando Never
:
func bar() -> Int {
if true {
abort() // No ERROR, but compiler sees it returns Never and warns:
return 2 // Will never be executed
} else {
return 1
}
}
Usamos Void
para decirle al compilador que no hay valor de retorno . La aplicación sigue funcionando.
Usamos Never
para decirle al compilador que no hay retorno al sitio de la persona que llama . La aplicación runloop se termina.
Vacío
Void es en sí mismo un tipo de retorno que es una tupla con cero elementos. Puedes usar Void y () indistintamente.
Mira estos ejemplos,
func yourFunc() {}
Esta es una función sin tipo de retorno, que básicamente devuelve una tupla con cero elementos, que se puede escribir como ()func yourFunc() -> Void {}
Función que informa explícitamente al compilador sobre el tipo de devolución de voidfunc yourFunc() -> () {}
Este tipo de retorno de () muestra lo mismo que el tipo void. () indica una tupla con cero elementos
Nunca
Nunca devolver-tipo informa al compilador que no existe la necesidad de devolver una tupla (). Además, la función con el tipo de nunca retorno se usa para el punto de salida de la ejecución actual, como un bloqueo, un error fatal, un aborto o una salida.
Para una comprensión detallada de nunca , echemos un vistazo a un ejemplo abort ():
1.
func yourFunc() {
abort()
print("Will not reach at this point") //Warning for this line
}
2.
func yourFunc() -> Int {
if true {
abort()
} else {
return 1
}
}
De los fragmentos de código anteriores, podemos ver cuando llamamos abort () (que no devuelve un valor) como la última instrucción en una función que espera que se devuelva un valor (en nuestro caso Int). El compilador no genera una advertencia.
abortar()
public func abort() -> Never
Similarmente para exit ():
public func exit(_: Int32) -> Never
La documentación de Apple dice: " Use Nunca como el tipo de retorno al declarar un cierre, función o método que incondicionalmente arroja un error, atrapa o no termina " .
Entonces, si desea escribir una función personalizada que registre un error catastrófico , debe usar el tipo de retorno Nunca para señalar al compilador:
func catastrophicErrorDisplay(error: String) -> Never {
DisplaySomeCustomLogFacility(error)
}
En resumen " Nunca se usa para fallas repentinas y totales de las cuales la recuperación es imposible " .