Matching error or result in while let

Hello Everyone
I am new in Rust.

I am doing a Hangman game in Rust which reads the words from a file and ask to the user to guess the word by asking the user to enter a character by stdin.

I have the next Structs

pub struct Hangman<'a> {
        pub word: &'a String,
        pub remaining_attempts: i32,
        pub used_chars: &'a mut HashMap<char, ()>,
        pub wrong_chars:&'a mut  HashMap<char, ()>
    }

pub enum GameStatus {
        Success,
        Pending,
        GameOver
}
pub struct Stats {
        guessed: String,
        guessed_char: char,
        status: GameStatus
 }

I also have a play method in the impl of Hangman

pub fn play(&mut self, char: char) -> Result<Stats, GameError> {
            let mut guessed: Vec<String> = vec![String::from("_"); self.word.len()];
            // println!("Ingresa una letra");
            let mut input_letter = char;
            let mut found: bool = false;
            let mut stats: Stats = Stats{
                guessed: "".to_string(),
                status: GameStatus::Pending,
                guessed_char: '\0'
            };

            if !self.can_play() {
                return Err(GameError::NoChancesAvailable);
            }

            if self.is_char_already_used(&input_letter) {
                return Err(GameError::CharacterIsAlreadyUsed);
            }

            for (i, c) in (self.get_word_to_guess()).chars().enumerate() {
                if c == input_letter {
                    found = true;
                    guessed[i] = c.to_string();
                }
            }

            self.use_char(input_letter);

            if found {
                stats.guessed_char = input_letter;
            } else {
                self.add_wrong_char(input_letter);
            }

            if guessed.join("") == *self.get_word_to_guess() {
                stats.status = GameStatus::Success;
            }

            self.substract_remaining_attempts();

            return Ok(stats);

        }

/error.rs

use std::fmt;

#[derive(Debug)]
pub enum GameError {
    NoChancesAvailable,
    CharacterIsAlreadyUsed,
}

impl std::error::Error for GameError {}

impl fmt::Display for GameError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            GameError::NoChancesAvailable => write!(f, "No more chances"),
            GameError::CharacterIsAlreadyUsed => write!(f, "Character is already used"),
        }
    }

In main.rs I have this function:

fn play(ahorcado: &mut hangman) {
    println!("Please input a char");
    let mut input_char = get_char();
    let mut game = hangman.play(input_char);
}

I would like to loop the hangman.play(input_char) call while stats.stats == GameStatus::Pending
or while GameError::CharacterIsAlreadyUsed error is returned.

I tried a couple of things like while let (while let - Rust By Example)

and also Pattern Matching but I can't achieve what I'm looking for. I got a lot of compiler errors.

I am sorry if I have a lot of bad practices. I just started with the language I am still learning about lifetimes, scopes, etc.

Thanks!

Here are some things which may be helpful:

  • Hangman should probably just own all of its data by removing all the &s and lifetimes from the struct. This will simplify the program a lot already.
  • If you don't need the keys of a HashMap, use a HashSet
  • Use clippy by running cargo clippy to get automated suggestions on how to improve your code

As for the loop, I would suggest using loop and explicitly returning when you detect that the game is over. Something like this

fn play(hangman: &mut Hangman) {
    loop {
        println!("Please input a char");
        let input_char = get_char();
        let game = hangman.play(input_char);
        // do something here to detect if the game is over and return if so
    }
}

3 Likes

Really thanks for the answer!!

I followed your hints and I ended up to:

loop {
        println!("turnos restantes: {}", ahorcado.get_remaining_attempts());
        println!("Ingresa una letra");
        let input_char = get_char();
        let game = ahorcado.play(input_char);
        match &game  {
                Ok(stats) => {
                    match stats.status {
                        GameStatus::Success => {
                            println!("Felicitaciones adivino la palabra");
                            println!("La palabra era: {}", ahorcado.word);
                            break;
                        }
                        GameStatus::Pending => {
                            println!("Mal!!.");
                        }
                        GameStatus::GameOver => {
                            println!("Game over");
                            break;
                        }
                        GameStatus::CharGuessed => {
                            println!("Adivino la siguiente letra: {}", stats.guessed_char);
                            println!("La palabra hasta ahora es: {}", stats.get_guessed_word());
                        }
                    }
                },
                Err(e) => {
                    match e {
                        GameError::NoChancesAvailable => {
                            eprintln!("Error: {}", e);
                            break;
                        },
                        GameError::CharacterIsAlreadyUsed => eprintln!("Error: {}", e),
                    }
                }
            }
    }

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.