Bienvenidos sean a este post, hoy hablaremos sobre como manejar errores con este enum.
En el post anterior hablamos sobre panic donde interrumpiremos la ejecucion ante cualquier error que pueda surgir en nuestro codigo pero no siempre debe ser asi y en determinados momentos debemos poder manejar este tipo de errores, un ejemplo seria no encontrar un archivo al quere abrirlo y en lugar de terminar el programa generamos uno nuevo, aqui entra nuestro conocido llamado Result.
Hemos mencionado y utilizado a Result en algunos posts anteriores porque tiene una particularidad de devolver dos tipos datos de forma generica, uno sera el tipo que deseamos manejar y otro vinculado a un error, veamos como es su definicion en el lenguaje:
enum Result<T, E>{
Ok(T),
Err(E),
}
Como dijimos maneja dos tipos genericos, el primero (T) al ser devuelto por un Ok sera el tipo que necesitamos trabajar y devuelto en caso de no haber ninguna falla, tal como dijimos antes, en cambio el segundo (E) al ser devuelto por Err sera para devolver un valor de error y que se procese como tal, cuando hablamos del manejo de archivos en este post mencionamos que devolvia un valor de tipo Result, para poder manejar la salida en caso de un error, para entenderlo vamos a utilizar el proyecto que creamos en el post anterior, en caso de no tenerlo simplemente hagan uno nuevo que se llame errores, ingresen al directorio del proyecto y generen un archivo de texto llamado hola.txt y le pondremos el siguiente texto:
hola.txt
tinchicus.com es el mejor lugar para aprender
O algun otro texto 😉 con nuestro archivo de texto creado procedamos a modificar el codigo del archivo main.rs de la siguiente manera:
main.rs
use std::fs::File;
fn main()
{
let f: u32 = File::open("hola.txt");
}
En este caso haremos un codigo simple donde abriremos el archivo que creamos anteriormente pero observen que la variable donde la almacenamos es de tipo u32, compilemos y veamos su salida:
tinchicus@dbn001vrt:~/lenguajes/rust/errores$ cargo build
Compiling errores v0.1.0 (/home/tinchicus/lenguajes/rust/errores)
error[E0308]: mismatched types
--> src/main.rs:5:15
|
5 | let f: u32 = File::open("hola.txt");
| --- ^^^^^^^^^^^^^^^^^^^^^^ expected `u32`, found enum `Result`
| |
| expected due to this
|
= note: expected type `u32`
found enum `Result<File, std::io::Error>`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `errores` due to previous error
tinchicus@dbn001vrt:~/lenguajes/rust/errores$
En este caso fallo por lo que dijimos anteriormente al no coincidir los tipos nos devuelve un error, para solucionarlo simplemente saquen la definicion del tipo y que se ajuste automaticamente, vamos a tomar el codigo anterior y lo modificaremos de la siguiente manera:
main.rs
use std::fs::File;
fn main()
{
let f = File::open("hola.txt");
let f = match f {
Ok(archivo) => archivo,
Err(error) => {
panic!("Hubo un inconveniente: {:?}", error)
},
};
}
Como dijimos antes eliminamos el tipo para que funcione pero observen un detalle volvemos a definir a la variable pero utilizamos un match de la definicion anterior, donde chequeamos si salio bien la apertura del archivo y lo devolvemos como tal u ocurrio un error y debemos notificarlo por medio de un panic, y comentando cual es, si lo compilamos deberia funcionar aunque no haria nada y a lo sumo nos notificara que f no es usado nunca pero funcionara, ahora podemos renombrar el archivo que queremos abrir o eliminarlo para ver que sucede, hagan una de las dos acciones, compilemos y veamos que sucede:
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
Running `target/debug/errores`
thread 'main' panicked at 'Hubo un inconveniente: Os { code: 2, kind: NotFound, message: "No such file or directory" }', src/main.rs:10:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
tinchicus@dbn001vrt:~/lenguajes/rust/errores$
Aca podemos ver como entro en panico el codigo porque no encontro el archivo pero esto lo podemos mejorar y para ello debemos modificar el codigo de la siguiente manera:
main.rs
use std::fs::File;
use std::io::ErrorKind;
fn main()
{
let f = File::open("hola.txt");
let f = match f {
Ok(archivo) => archivo,
Err(ref error) if error.kind() == ErrorKind::NotFound => {
match File::create("hola.txt") {
Ok(ac) => ac,
Err(e) => {
panic!(
"Fallo al crear archivo: {}", e)
},
}
},
Err(error) => {
panic!("Hubo un inconveniente: {:?}", error)
},
};
}
Nuestra primera modificacion es el agregado de una nueva libreria para poder manejar los errores como es ErrorKind, la siguiente sera agregar una nueva opcion para el match veamos cual es:
Err(ref error) if error.kind() == ErrorKind::NotFound => {
match File::create("hola.txt") {
Ok(ac) => ac,
Err(e) => {
panic!(
"Fallo al crear archivo: {}", e)
},
}
},
Aqui analizaremos un error y la almacenaremos en la parte alta de la memoria, a esta variable la chequearemos con un condicional mediante el metodo kind donde verificaremos si es del tipo NotFound, en caso de ser verdadero, es decir que el archivo no existe, procede a crear un nuevo archivo y aqui usaremos otro match donde verificamos si se pudo crear o no el archivo, en caso de ser verdadero procede a devolver de lo contrario nos informa que se fallo y el motivo, la ultima parte del codigo es similar al ejemplo anterior donde verifica si ocurrio algun error que no sea de tipo archivo no encontrado y lo muestra en pantalla, si lo compilan y ejecutan nos mostrara que no usamos a la variable f pero creara el archivo en caso de no existir, en cambio si nosotros modificamos los permisos del archivo ocurrira un error distinto, si estan Linux ejecuten el siguiente comando:
$ chmod 000 hola.txt
Esto nos quitara todos los permisos sobre el archivo, si estan en Windows vayan a las propiedades y quiten los permisos sobre el mismo (en caso de ser NTFS), una vez realizado la quita de permisos proceddan a ejecutar de nuevo el archivo y ahora nos devolvera el siguiente error:
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/errores`
thread 'main' panicked at 'Hubo un inconveniente: Os { code: 13, kind: PermissionDenied, message: "Permission denied" }', src/main.rs:20:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
tinchicus@dbn001vrt:~/lenguajes/rust/errores$
Aca podemos observar que el codigo ignoro el condicional donde verifica el archivo no encontrado y paso al general porque al no poder acceder al archivo por permiso denegado, observen que en la salida nos informa el kind, si lo agregamos como hicimos con el de archivo no encontrado (NotFound) tambien le podemos dar un tratamiento especial sin necesidad de terminar directamente.
En resumen, hoy hemos visto una alternativa a panic como es Result, que es, como se compone, como lo podemos usar, vimos un par de ejemplos simples para ponerlo en practica, tambien hemos visto como lo podemos usar en conjunto con panic y por ultimo como se podria usar con mas opciones, espero les haya sido util sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
