4
\$\begingroup\$

This is how I implemented the guessing game - chapter 2 of the Rust book: https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

use std::io;
use std::io::Error;
use std::cmp::Ordering;
use std::num::ParseIntError;

use rand::Rng;


enum MyReadLineError {
    FailReadLine(Error)
}

#[derive(Debug)]
enum MyReadU32Line {
    FailReadLine(Error),
    FailParse(ParseIntError)
}


fn my_read_line() -> Result<String, MyReadLineError> {
    let mut input = String::new();
    let result = io::stdin().read_line(&mut input);
    match result {
        Ok(_) => Ok(input),
        Err(error ) => Err(MyReadLineError::FailReadLine(error)),
    }
}

fn my_read_u32_line() -> Result<u32, MyReadU32Line> {
    match my_read_line() {
        Result::Ok(line) =>
            match line.trim().parse::<u32>() {
                Ok(value) => Ok(value),
                Err(error) => Err(MyReadU32Line::FailParse(error)),
            }

        Result::Err(MyReadLineError::FailReadLine(error)) => Err(MyReadU32Line::FailReadLine(error)),
    }
}

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1, 101);

    loop {
        println!("Please input your guess.");
        let guess = my_read_u32_line();
        match guess {
            Ok (value) => match value.cmp(&secret_number) {
                Ordering::Less => println!("Too small!"),
                Ordering::Greater => println!("Too big!"),
                Ordering::Equal => {
                    println!("You win!");
                    break;
                },
            }
            Err (_) => println!("Sorry I couldn't read a u32 from your input! Please try again."),
        }
    }
}

This works as expected but I would like to flatten the matches so that doesn't look at verbose, is that possible. Also is there any other way to make that code look better, more readable?

\$\endgroup\$
6
\$\begingroup\$

Welcome to Rust!

Code Formatting

There are some inconsistencies in your formatting — I have run cargo fmt to get rid of them.

Naming

There's no need to prefix everything with My or my_ — in Rust, your names won't as easily clash with existing names as might be the case in certain other languages.

Error handling

For applications, I suggest using the anyhow crate to manage errors — it reduces much of the boilerplate. Just add

anyhow = "1.0"

to the [dependencies] section of your Cargo.toml.

Helper functions

The my_read_line function can be simplified with the ? operator:

use anyhow::Result;

fn read_line() -> Result<String> {
    let mut input = String::new();
    io::stdin().read_line(&mut input)?;
    Ok(input)
}

my_read_u32_line can be similarly simplified:

fn read_u32() -> Result<u32> {
    Ok(read_line()?.trim().parse()?)
}

Magic numbers

We can define constants for the range of the number:

const MIN: u32 = 1;
const MAX: u32 = 100;

// later
let secret_number = rand::thread_rng().gen_range(MIN, MAX + 1);

Organization

I might reorganize the main function to reduce indentation:

loop {
    println!("Please input your guess.");

    let guess = match read_u32() {
        Ok(guess) => guess,
        Err(_) => {
            println!(
                "Sorry I couldn't read a u32 from your input! \
                Please try again."
            );
            continue;
        }
    };

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => {
            println!("You win!");
            break;
        }
    };
}

Final result

use anyhow::Result;
use rand::Rng;
use std::cmp::Ordering;
use std::io;

const MIN: u32 = 1;
const MAX: u32 = 100;

fn read_line() -> Result<String> {
    let mut input = String::new();
    io::stdin().read_line(&mut input)?;
    Ok(input)
}

fn read_u32() -> Result<u32> {
    Ok(read_line()?.trim().parse()?)
}

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(MIN, MAX + 1);

    loop {
        println!("Please input your guess.");

        let guess = match read_u32() {
            Ok(guess) => guess,
            Err(_) => {
                println!(
                    "Sorry I couldn't read a u32 from your input! \
                    Please try again."
                );
                continue;
            }
        };

        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Greater => println!("Too big!"),
            Ordering::Equal => {
                println!("You win!");
                break;
            }
        };
    }
}
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thanks for your answer, this is really awesome!!! Real nice of you =] \$\endgroup\$ – Kerry Sep 24 at 18:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.