hello,
last time I made a fibonacci series generator in Rust and now I have made something different :)
use std::io;
fn main() {
let mut input: String = String::new();
let stdin = io::stdin();
let x = rand::random::<u32>() % 101;
let mut attempts = 0;
loop {
println!("Guess a number from 0 to 100:");
stdin.read_line(&mut input);
input = input.to_string().replace("\n", ""); // removing the \n
let user_input: u32 = input.parse::<u32>().unwrap();
if x == user_input {
println!("You won! attempts: {attempts}");
break;
}
else if x < user_input {
println!("too big");
attempts += 1;
}
else {
println!("too small");
attempts += 1;
}
input.clear()
}
}
feel free to give me suggestion :)
The biggest/only real problem is the
.unwrap()
. This will cause the program to exit with an error message if the user inputs an invalid value. Which is not a great user experience. Better to reject the input and ask them again (aka start the loop again). You can do this with amatch
on the returned value of parse with a message and a continue onErr
(good time to learn about enums and pattern matching as well).A minor improvement can be done to this:
input = input.to_string().replace("\n", ""); // removing the \n let user_input: u32 = input.parse::<u32>().unwrap();
By replacing it with:
let user_input: u32 = input.trim().parse::<u32>().unwrap();
Which has a few advantages:
- it removes all white space, including tabs and spaces from both the start and end.
- it requires no allocations.
- it is one fewer lines while still being clear what it is doing.
- no mutability needed (though input still needs to be mutable here in other cases this can be a benefit).
These are more nitpicks than anything substantial - would lead to nicer code in more complex situations but negligible at best in this code base (only really pointing out to give you some things to think about):
Since you break in the win condition the
attempts += 1;
can be moved out of the other branches at the same level as theinput.clear()
.Rand has a gen_range function which can be used like:
rand::thread_rng().gen_range::<u32>(0..=100);
Though typically you would save the thread_rng() output to a local and reuse that throughout your program instead of creating one each time - here you only need one number so that matters less. The Rng trait has a lot more convenience function on it as well so it is generally worth using over just the
random()
function. Though in this case it makes very little difference overall here - typically you would want to opt for one of those methods instead so is worth knowing about.I dislike the name
x
as it is very ambiguous. IMO short letters should be used only in smaller scopes or when they represent some sort of mathematical value. Here I would usesecret_number
orsecret_value
or something more descriptive. Though in this case there is not a much better domain name here, more often there is.Thank you so much for the suggestion. I have updated the code locally :)
Some suggestions:
- Declare
input
in the loop. That limits scope which is basically always a good idea. - Trim the newline like
let input = input.trim();
This is better in several ways:
- Simpler
- Doesn’t allocate a new string.
- Removes all whitespace at the start and end.
- It makes
input
immutable after that line.
Also
attempts
is never read - you should have got a compiler warning about that.Otherwise, good work.
Thank you for the suggestions.
attempts
is used to display total attempts when user wins the game so it is used in the program, I don’t see any warnings from compiler.Ah yeah you’re right - I missed that.
Declare input in the loop. That limits scope which is basically always a good idea.
There is good reason to not do this. Though in this case it won’t make much difference as performance is not an issue. But by having it outside the loop it allows the allocation of the string to be reused and in more complex programs is typically what you would want to do - allocate once outside the loop and reuse that inside.
In this case waiting on user input is going to outweigh any performance benefit I would not call it out as bad practice either.
Yes this is true, but it’s a little advanced for this level so I didn’t mention it. Probably better for beginners to focus on good code style than optimal performance.
- Declare
@kionite231
You could save the inputs done by the user and ask if the guesses should be listed after you did enter the correct number.done :D
use std::io; fn main() { let mut input: String = String::new(); let stdin = io::stdin(); let x = rand::random::<u32>() % 101; let mut attempts = 0; let mut user_inputs: Vec<u32> = Vec::new(); loop { println!("Guess a number from 0 to 100:"); stdin.read_line(&mut input); input = input.to_string().replace("\n", ""); // removing the \n let user_input: u32 = input.parse::<u32>().unwrap(); user_inputs.push(user_input); if x == user_input { println!("You won! attempts: {attempts}"); println!("Your inputs:"); for input in user_inputs { print!("{input} "); } println!(""); break; } else if x < user_input { println!("too big"); attempts += 1; } else { println!("too small"); attempts += 1; } input.clear() } }
@kionite231
You could also color the inputs in different colorsMaybe red if it was wrong and green if the guess was right.
You could also keep that color coding if the list of inputs is getting shown at the end.
but to do that I have to use external deps? which I am not comfortable doing. (I am newbie :) )
Nice work and congrats on your progress! Being new and uncomfortable with dependencies, this project might be a good opportunity to read and apply chapter 7 of the rust book if you haven’t already. Specifically 7.2 defining modules … and the sections that follow.
As your projects increase complexity it’s really useful to shift chunks around (within main.rs and into other files/directories). Doing it now will make it easier to see the individual parts (vs waiting till a project has a lot going on). It might make dependencies feel less unfamiliar.
Coloured text does not require a dep. It is just about printing the right colour codes like:
const BLACK: &'static str = "\u{001b}[30m"; const RED: &'static str = "\u{001b}[31m"; const GREEN: &'static str = "\u{001b}[32m"; const YELLOW: &'static str = "\u{001b}[33m"; const BLUE: &'static str = "\u{001b}[34m"; const MAGENTA: &'static str = "\u{001b}[35m"; const CYAN: &'static str = "\u{001b}[36m"; const WHITE: &'static str = "\u{001b}[37m"; const CLEAR: &'static str = "\u{001b}[0m"; fn main() { println!("{RED}red! {BLUE}blue!{CLEAR}"); }
The libraries just make it easier so you don’t need to remember or know what those codes mean.
I thought, colour codes are platform dependent, will it work on windows
I usually run things on Linux or macOS, but using a library (crate) may add portability, imo
@kionite231
Okay I do understand that maybe I will get another idea if so I will let you know.