¿Cómo capturar un evento de teclado en Rust?

Publicado por Brisa
hace 5 meses

Hola a todos,

Estoy intentando capturar eventos de teclado en Rust, pero no logro encontrar una solución que funcione. He investigado un poco y he visto que hay una biblioteca llamada "crossterm" que parece ser popular para trabajar con la entrada y salida de la consola en Rust. Sin embargo, no estoy seguro de cómo utilizarla para capturar eventos de teclado específicamente.

Aquí está el código que he intentado hasta ahora:

use crossterm::event::{read, KeyCode, KeyEvent, KeyModifiers};

fn main() {
    loop {
        match read() {
            Ok(event) => {
                match event {
                    KeyEvent {
                        code: KeyCode::Char('q'),
                        modifiers: KeyModifiers::CONTROL,
                    } => {
                        println!("¡Has presionado Ctrl+Q!");
                        break;
                    }
                    _ => println!("Otro evento de teclado"),
                }
            }
            Err(e) => println!("Error: {:?}", e),
        }
    }
}

Cuando intento compilar este código, obtengo el siguiente error:

error[E0277]: the trait bound `char: std::str::FromStr` is not satisfied
  --> src/main.rs:9:42
   |
9  |                       code: KeyCode::Char('q'),
   |                                        ^^^ the trait `std::str::FromStr` is not implemented for `char`
   |
   = help: the following implementations were found:
             <char as std::str::FromStr>::from_str(::core::str::&str)
   = note: required by `std::str::FromStr::from_str`

¿Alguien puede ayudarme a entender por qué está ocurriendo este error y cómo puedo resolverlo?

¡Gracias de antemano!

Rust Evento de teclado Capturar
Respuesta de Gordon Shumway
hace 5 meses

¡Hola Brisa!

El error que estás viendo se debe a una confusión con la sintaxis. En Rust, el error indica que estás intentando utilizar un trait FromStr para un char, lo cual no es necesario aquí. De hecho, el problema no está directamente relacionado con el trait FromStr sino con cómo estás intentando acceder a la estructura KeyEvent.

Probablemente el error se deba a un malentendido común al tratar de hacer pattern matching en Rust. Al hacer match con estructuras en Rust, no puedes directamente detallar los nombres de los campos y sus valores como en otros lenguajes, a menos que uses el patrón de estructura (usando {} y no ()).

Además, para trabajar con eventos de teclado utilizando crossterm, debes tener en cuenta varios detalles: primero, asegurarte de haber incluido la biblioteca crossterm en tu Cargo.toml, y segundo, entender que la estructura KeyEvent ha cambiado en versiones recientes de crossterm, lo que quizás esté causando confusión.

Suponiendo que estás usando la versión 0.20 o posterior de crossterm, la forma correcta de hacer match con un evento de teclado sería algo así:

use crossterm::event::{read, Event, KeyCode, KeyModifiers};

fn main() {
    loop { 
        match read() { 
            Ok(event) => match event { 
                Event::Key(key_event) => {
                    if key_event.code == KeyCode::Char('q') && key_event.modifiers.contains(KeyModifiers::CONTROL) {
                        println!("¡Has presionado Ctrl+Q!");
                        break;
                    } else {
                        println!("Otro evento de teclado");
                    }
                },
                _ => println!("Evento no relacionado al teclado"),
            },
            Err(e) => println!("Error: {:?}", e),
        } 
    }
}

Aquí, clave es entender que KeyEvent ahora está dentro de Event::Key. Por lo tanto, primero debes hacer match con Event::Key y luego con el struct interno KeyEvent. Además, estoy usando key_event.modifiers.contains(KeyModifiers::CONTROL) para verificar si la tecla Control está presionada, lo cual es una forma más idiomatica de hacerlo en Rust.

Prueba este código, y si tienes crossterm en tu Cargo.toml con la versión adecuada, debería funcionar como esperas. ¡Espero que esto te ayude!