¿Dónde está el error, Billy? Necesitamos un error ...


Hace algún tiempo, mi colega publicó un artículo sobre el manejo de errores en Java / Kotlin. Y me resultó interesante qué métodos de transmisión de errores existen en la programación en general. Si también está interesado, entonces debajo del corte es el resultado de la investigación. Lo más probable es que se hayan omitido algunos métodos exóticos, pero aquí solo hay una esperanza para los comentarios, que a veces son más interesantes y útiles en el artículo de Habré. :)

En la historia de los lenguajes de programación, no se han inventado muchas formas para transmitir un error. Si nos desconectamos por completo, solo hay tres de ellos: un retorno directo de una función, una transferencia de control y la configuración del estado. Todo lo demás es, en un grado u otro, una mezcla de estos enfoques. A continuación intenté reunir y describir a los principales representantes de estas tres especies.

Descargo de responsabilidad: por brevedad y simplificación de la percepción de cualquier código ejecutable aislado que genere un error, utilizaré la palabra "función", y para cualquier entidad no primitiva (entero, cadena, booleana, etc.) - "estructura".

Retorno directo


El retorno directo es simple. Aunque este es probablemente el método más utilizado, hay muchas opciones. El método de procesamiento los une a todos, comparando el valor de retorno con valores predefinidos.

  1. Devolver el estado de ejecución. La opción más banal es VERDADERO (si se ejecutó normalmente) o FALSO (si hubo un fallo).
  2. Devuelve el valor correcto en caso de éxito e incorrecto en caso de error.
    C / c ++
    La función strchr () devuelve un puntero a la primera aparición del carácter ch en la cadena a la que apunta str. Si no se encuentra ch, se devuelve NULL.

    Muy a menudo, los enfoques 1 y 2 se utilizan junto con la configuración del estado.
  3. Código de error de retorno. Si queremos saber no solo que la ejecución terminó incorrectamente, sino también entender dónde ocurrió el error en la función. Por lo general, si la función se completa sin error, se devuelve el código 0. En el caso de un error, el código se usa para determinar un lugar específico en el cuerpo de la función donde algo salió mal. Pero esta no es una regla de hierro, mira, por ejemplo, HTTP con su 200.
  4. Devuelve el código de error en un rango de valores no válido. Por ejemplo, normalmente una función debería devolver un número entero positivo y, en caso de error, su código con un signo menos.

    function countElements(param) { if (!isArray(param)) { return -10; } else if(!isInitialized(param)){ return -20 } else { return count(array); } } 

  5. Devuelve diferentes tipos para obtener resultados positivos y negativos. Por ejemplo, nominalmente - una cadena, pero no nominalmente - un número o clase Éxito y clase Error .

     sealed class UserProfileResult { data class Success(val userProfile: UserProfileDTO) : UserProfileResult() data class Error(val message: String, val cause: Exception? = null) : UserProfileResult() } val avatarUrl = when (val result = client.requestUserProfile(userId)) { is UserProfileResult.Success -> result.userProfile.avatarUrl is UserProfileResult.Error -> "http://domain.com/defaultAvatar.png" } 

    También puede recordar cualquiera del mundo de la programación funcional. Aunque aquí puedes discutir.
  6. Devolver una estructura que contiene tanto el resultado en sí como el error.

     function doSomething(): array { ... if($somethingWrong === true) { return ["result" => null, "error" => "Alarm!!!"]; } else { return ["result" => $result, "error" => null]; } ... } 

  7. Devuelve múltiples valores. Al principio me sentí inclinado a no separar este método del anterior, pero al final decidí ponerlo en un párrafo separado. Esta opción es bastante rara, ya que puede usarse exclusivamente en idiomas que le permiten devolver varios valores de una función, y no hay muchos de ellos. El ejemplo más llamativo, pero no el único, es el lenguaje Go .

     f, err := Sqrt(-1) if err != nil { fmt.Println(err) } 

Ajuste de estado


La versión más antigua y hardcore, que no ha perdido su relevancia hasta el día de hoy. Consiste en el hecho de que la función no devuelve nada y, en caso de error, escribe su valor (en cualquier forma) en una entidad separada, ya sea un registro de procesador, una variable global o un campo de clase privada. Para manejar este tipo de error, debe extraer de forma independiente el valor del lugar correcto y verificarlo.

  1. Establecer el estado "global". Lo tomé entre comillas porque la mayoría de las veces estamos hablando de la globalidad en cierto ámbito.

     # ls /unknown/path 2>/dev/null # echo $? 1 

  2. Estableciendo tu propio estado. Cuando tenemos alguna estructura que proporciona una función. La función establece el estado de esta estructura, y el error ya se extrae de la estructura directamente o mediante otra función especializada.

     $mysqli = new mysqli("localhost", "my_user", "my_password", "world"); $result = $mysqli->query("SET a=1"); if ($mysqli->errno) { printf(" : %d\n", $mysqli->errno); } 

  3. Establecer el estado del objeto devuelto. Resuena fuertemente con el párrafo 6. de la sección anterior. A diferencia del párrafo anterior, se realiza una verificación de estado en la estructura devuelta, y no en la que proporciona la función. Como un ejemplo obvio, el protocolo HTTP y las innumerables bibliotecas en una amplia variedad de idiomas funcionan con él.

     Response response = client.newCall("https://www.google.com").execute(); Integer errorCode = response.getCode(); 


Transferencia de control


Y ahora llegamos al paradigma más de moda. Excepciones, devoluciones de llamada, controladores de errores globales: todo esto. Lo que los une a todos es que, en caso de error, el control se transfiere a un controlador predeterminado y no al código que llamó a la función.

  1. Excepciones Todos saben tirar / intentar / atrapar. Lanzando una excepción, la función forma una estructura que describe el error y, con mayor frecuencia, contiene varios metadatos útiles que facilitan el diagnóstico del problema (por ejemplo, la pila de llamadas). Después de eso, esta estructura se pasa a un mecanismo especial que "retrocede" a lo largo de la pila de llamadas al primer bloque de prueba, que está asociado con catch, que puede manejar excepciones de este tipo. Este método es bueno porque el tiempo de ejecución mismo implementa toda la lógica de lanzar una excepción. Lo mismo es malo, ya que los gastos generales (solo sin holivarov :)).
  2. Manejadores de errores globales. No es la forma más común, pero lo hace. Ni siquiera sé qué decir aquí. Es posible notar que los mecanismos de los navegadores también se pueden atribuir aquí: cuando el código que funciona fuera de la secuencia principal monitorea los eventos que llegan a ella.

     function myErrorHandler($errno, $errstr, $errfile, $errline) { echo "<b>Custom error:</b> [$errno] $errstr<br>"; echo " Error on line $errline in $errfile<br>"; } set_error_handler("myErrorHandler"); 

  3. Callbacks Los desarrolladores de Android, JavaScript y los apologistas de la programación reactiva los aman mucho. La esencia es simple: además de los datos procesados, las funciones del controlador se transfieren a la función. En caso de error, la función principal llamará al controlador correspondiente y le transmitirá el error.

     var observer = Rx.Observer.create( x => console.log(`onNext: ${x}`), e => console.log(`onError: ${e}`), () => console.log('onCompleted')); 


Parece no haber olvidado nada.

Y un hecho curioso. Probablemente la forma más original de devolver un error, combinando al mismo tiempo excepciones, estableciendo el estado y devolviendo varios valores, me encontré en Informix SPL (escribo de memoria):

 CREATE PROCEDURE some_proc(...) RETURNING int, int, int, int; … ON EXCEPTION SET SQLERR, ISAMERR RETURN 0, SQLERR, ISAMERR, USRERR; END EXCEPTION; LET USRERR = 1; -- do Something That May Raise Exception LET USRERR = 2; -- do Something Other That May Raise Exception … RETURN result, 0, 0, 0; END PROCEDURE 

Source: https://habr.com/ru/post/472506/


All Articles