Rust Beginner: aplatir les correspondances imbriquées

Sep 20 2020

Voici comment j'ai implémenté le jeu de devinettes - chapitre 2 du livre Rust: 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."),
        }
    }
}

Cela fonctionne comme prévu, mais j'aimerais aplatir les correspondances afin que cela ne semble pas verbeux, est-ce possible. Existe-t-il également un autre moyen de rendre ce code meilleur, plus lisible?

Réponses

7 L.F. Sep 20 2020 at 21:04

Bienvenue à Rust!

Formatage du code

Il y a quelques incohérences dans votre formatage - j'ai couru cargo fmtpour m'en débarrasser.

Appellation

Il n'est pas nécessaire de tout préfixer avec Myou my_- dans Rust, vos noms ne seront pas aussi facilement en conflit avec les noms existants que cela pourrait être le cas dans certaines autres langues.

La gestion des erreurs

Pour les applications, je suggère d'utiliser la anyhowcaisse pour gérer les erreurs - cela réduit une grande partie du passe-partout. Il suffit d'ajouter

anyhow = "1.0"

à la [dependencies]section de votre Cargo.toml.

Fonctions d'assistance

La my_read_linefonction peut être simplifiée avec l' ?opérateur:

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 peut être simplifié de la même manière:

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

Numéros magiques

Nous pouvons définir des constantes pour la plage du nombre:

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

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

Organisation

Je pourrais réorganiser la mainfonction pour réduire l'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;
        }
    };
}

Résultat final

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;
            }
        };
    }
}