Adrianistán

El blog de Adrián Arroyo


Leer de teclado en Rust

- Adrián Arroyo Calle

En muchas aplicaciones es necesario leer datos de teclado. Si bien esto podría considerarse sencillo en lenguaje como Python o Ruby, lo cierto es que sí se quiere hacer bien es complicado. Funciones como scanf de C son consideradas inseguras y en Java, hasta la llegada de la clase java.util.Scanner era un dolor de cabeza. En Rust no es distinto, es por ello que muchos tutoriales de Rust obvian esta parte. No obstante, leer de teclado no es tan difícil, como veremos a continuación.


read_line


El método principal para leer de teclado es read_line, que nos lee una línea como String. Para acceder a read_line primero necesitamos tener on objeto stdin. La manera más fácil de hacerlo es usar el módulo std::io.

El procedimiento es el siguiente, en primer lugar creamos una variable de tipo String vacía y mutable donde se va a alojar el resultado, posteriormente leemos y tratamos el resultado.


use std::io;

fn main() {
println!("Dime tu nombre: ");
let mut input = String::new();
io::stdin().read_line(&mut input);
println!("Tu nombre es {}",input.trim());
}


Como vemos, al leer la línea también se nos guarda el salto de línea. Si queremos quitarlo podemos usar trim.

Este código sin embargo generará una advertencia por el compilador y es que read_line genera devuelve un valor, concretamente un Result, que como vimos, sirven para el manejo de errores en Rust. Si no queremos tratar este Result con especial interés, podemos usar ok y opcionalmente especificar un mensaje de error con expect.


use std::io;

fn main() {
println!("Dime tu nombre: ");
let mut input = String::new();
io::stdin().read_line(&mut input).ok().expect("Error al leer de teclado");
println!("Tu nombre es {}",input.trim());
}


Si quieres tratar el error mejor puedes, pero read_line no suele fallar.

Leyendo enteros


Hasta aquí todo sencillo, porque leíamos String, en lo que entra todo lo que el usuario puede meter. Pero, ¿y si queremos leer un número de teclado? La cosa se complica. Normalmente se lee de teclado como String y luego se intenta pasar a número. Veamos como.


use std::io;
use std::str::FromStr;

fn main() {
println!("Dime tu edad: ");
let mut input = String::new();
io::stdin().read_line(&mut input).ok().expect("Error al leer de teclado");
let edad: u32 = u32::from_str(&input.trim()).unwrap();
let frase = if edad >= 18 {
"Mayor de edad"
}else{
"Menor de edad"
};
println!("{}",frase);
}


Como vemos, hay que importar std::str::FromStr para tener disponible las operaciones from_str en los tipos elementales. También se observa que hemos hecho un unwrap, porque from_str devuelve un Result. Este error sin embargo conviene que lo tratemos con más cuidado, pues es bastante probable que salte.

Un ejemplo ideal


En este código vamos a ver como pedir un entero, asegurándonos de que el usuario realmente introduce un entero e insistiendo hasta que finalmente introduce un entero válido.


use std::io;
use std::io::Write;
use std::str::FromStr;
use std::num::ParseIntError;

fn read_input() -> Result<u32,ParseIntError> {
print!("Dime tu edad: ");
io::stdout().flush().ok();
let mut input = String::new();
io::stdin().read_line(&mut input).ok().expect("Error al leer de teclado");
let input = input.trim();
let edad: u32 = u32::from_str(&input)?;
Ok(edad)
}

fn main() {
let edad;
loop {
if let Ok(e) = read_input(){
edad = e;
break;
}else{
println!("Introduce un número, por favor");
}
}
let frase = if edad >= 18 {
"Mayor de edad"
}else{
"Menor de edad"
};
println!("{}",frase);
}


He decidido separar la parte de pedir el número a otra función que devuelve Result para así poder usar el operador ?. También he usado print! y io::stdout().flush() en vez de println! para que tanto el mensaje como la entrada se realice en la misma línea y quede más bonito.



 

Añadir comentario

Todos los comentarios están sujetos a moderación