Rust Beginner: aplatir les correspondances imbriquées
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
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;
}
};
}
}