Un error es básicamente un comportamiento o evento inesperado que puede hacer que un programa produzca resultados no deseados o termine abruptamente. Los errores son cosas que nadie quiere en su programa. Podemos intentar encontrar y analizar partes del programa que puedan causar errores. Una vez que encontramos esas partes, podemos definir cómo deben comportarse esas partes si encuentran un error. Este proceso de encontrar y definir casos para un bloque particular de código es lo que llamamos Manejo de Errores . Una cosa que debemos tener en cuenta es que no podemos deshacernos por completo de los errores, pero podemos intentar minimizarlos o al menos reducir su efecto en nuestro programa.
En Rust, los errores se pueden clasificar en dos categorías, a saber, recuperables e irrecuperables.
- Errores recuperables: Los errores recuperables son aquellos que no hacen que el programa finalice abruptamente. Ejemplo: cuando intentamos recuperar un archivo que no está presente o no tenemos permiso para abrirlo.
- Errores irrecuperables: Los errores irrecuperables son aquellos que hacen que el programa finalice abruptamente. Ejemplo: intentar acceder a un índice de array mayor que el tamaño de la array.
La mayoría de los lenguajes no distinguen entre los dos errores y usan una clase de Excepción para superarlos, mientras que Rust usa un tipo de datos Result <R,T> para manejar errores recuperables y pánico. macro para detener la ejecución del programa en caso de errores irrecuperables.
¡Primero veremos cómo y dónde debemos usar el pánico! macro. Antes, veremos qué le hace a un programa.
Rust
fn main() { panic!("program crashed"); }
Producción:
thread 'main' panicked at 'program crashed', main.rs:2:7
Entonces, básicamente detiene la ejecución del programa e imprime lo que le pasamos en su parámetro.
¡pánico! la macro puede estar en los archivos de biblioteca que usamos, veamos algunos:
Rust
fn main() { let v = vec![1, 2, 3]; println!("{}",v[3]) }
Producción:
thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 3', main.rs:3:19
Dado que estamos tratando de acceder a elementos más allá de los límites del vector, ¡se llama pánico! macro.
Solo debemos usar panic en una condición si nuestro código puede terminar en mal estado. Un mal estado es cuando se ha roto alguna suposición, garantía, contrato o invariante, como cuando se pasan a nuestro código valores no válidos, valores contradictorios o valores faltantes y al menos uno de los siguientes:
- Si un mal estado ocurre una vez en una luna azul.
- Su código después de este punto debe depender de que no esté en este mal estado.
- No hay una buena manera de codificar esta información en los tipos que usa.
Errores recuperables
Result<T,E> es un tipo de datos de enumeración con dos variantes OK y Err que se define de la siguiente manera
enum Result<T, E> { Ok(T), Err(E), }
T y E son parámetros de tipo genérico donde T representa el tipo de valor que se devolverá en caso de éxito dentro de la variante Ok, y E representa el tipo de error que se devolverá en caso de falla dentro de la variante Err.
Rust
use std::fs::File; fn main() { let f = File::open("gfg.txt"); println!("{:?}",f); }
Producción:
Err(Os { code: 2, kind: NotFound, message: "No such file or directory" })
Dado que el archivo gfg.txt no estaba allí, File devolvió la instancia de Err. Si se hubiera encontrado el archivo gfg.txt, se habría devuelto una instancia del archivo.
Si no se encuentra un archivo como en el caso anterior, será mejor si le pedimos al usuario que verifique el nombre del archivo, la ubicación del archivo o que proporcione las especificaciones del archivo una vez más o lo que requiera la situación.
Rust
use std::fs::File; fn main() { // file doesn't exist let f = File::open("gfg.txt");/ match f { Ok(file)=> { println!("file found {:?}",file); }, Err(_error)=> { // replace it with whatever you want // to do if file is not found println!("file not found \n"); } } }
Producción:
file not found
En el programa anterior, básicamente coincide con el tipo de devolución del resultado y realiza la tarea en consecuencia.
Vamos a crear nuestros propios errores de acuerdo con la lógica empresarial. Supongamos que queremos producir un error si una persona menor de 18 años intenta solicitar una credencial de elector.
Rust
fn main(){ let result = eligible(13); match result { Ok(age)=>{ println!("Person eligible to vote with age={}",age); }, Err(msg)=>{ println!("{}",msg); } } } fn eligible(age:i32)->Result<i32,String> { if age>=18 { return Ok(age); } else { return Err("Not Eligible..Wait for some years".to_string()); } }
Producción:
Not Eligible..Wait for some years
Si queremos abortar el programa después de que encuentre un error recuperable, ¡podríamos usar panic! macro y para simplificar el proceso, Rust proporciona dos métodos unwrap() y expect().
- Desenvolver()
Rust
use std::fs::File; fn main() { let f = File::open("gfg.txt").unwrap(); }
Producción:
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Os { code: 2, kind: NotFound, message: "No such file or directory" }', main.rs:17:14
El unwrap() llama al panic! macro en caso de que no se encuentre el archivo mientras devuelve la instancia del controlador de archivos si se encuentra el archivo. Aunque unwrap() hace que el programa sea más corto, cuando hay demasiados métodos unwrap() en nuestro programa, entonces se vuelve un poco confuso en cuanto a qué método unwrap() llamó pánico. macro. Entonces necesitamos algo que pueda producir los mensajes personalizados. En ese caso, el método expect() viene al rescate.
- suponer()
Rust
use std::fs::File; fn main() { let f = File::open("hello.txt").expect("Failed to open gfg.txt"); }
Producción:
thread 'main' panicked at 'Failed to open gfg.txt: Os { code: 2, kind: NotFound, message: "No such file or directory" }', main.rs:17:14
¡Pasamos nuestro mensaje al pánico! macro a través del parámetro esperado.