How to overwrite mutable string inside a loop? - rust

My example is slightly modified from the guessing game tutorial in The Rust Book.
After the first iteration, the loop does not appear to read in the user input to the mutable string correctly.
Can you please identify what is wrong in the below code with regards to mut input_text?
extern crate rand;
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
let random_number = rand::thread_rng().gen_range(1, 51);
let mut input_text = String::new(); // Works fine if allocated inside loop
loop {
println!("Enter your guess:");
io::stdin()
.read_line(&mut input_text)
.expect("Failed to read input");
let input_number: u32 = match input_text.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!(
"You entered {} which converted to u32 is {}",
input_text, input_number
);
match input_number.cmp(&random_number) {
Ordering::Greater => println!("Input > Random"),
Ordering::Less => println!("Input < Random"),
Ordering::Equal => println!("Input == Random"),
}
}
}

As #Jmb mentioned in the comments, read_line doesn't overwrite the String but appends to it. If you'd like to overwrite the String you have to first call clear on it like so:
use rand::Rng;
use std::cmp::Ordering;
use std::io;
fn main() {
let random_number = rand::thread_rng().gen_range(1, 51);
let mut input_text = String::new();
loop {
println!("Enter your guess:");
// clear input_text from previous loop iteration
input_text.clear();
io::stdin()
.read_line(&mut input_text)
.expect("Failed to read input");
let input_number: u32 = match input_text.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
println!(
"You entered {} which converted to u32 is {}",
input_text.trim(), input_number
);
match input_number.cmp(&random_number) {
Ordering::Greater => println!("Input > Random"),
Ordering::Less => println!("Input < Random"),
Ordering::Equal => {
println!("Input == Random");
break; // break on win condition
},
}
}
}
Also your program was an infinite loop so I added a break on the win condition.

Related

"mismatched types: expected `i16`, found `Result`" in a number guessing game

I am making a random number guessing program with Rust but when I check for an actual number I get an error that says "expected i16, found enum std::result::Result".
use rand::Rng; // 0.8.0
use std::io::{stdin, stdout, Write};
use std::process;
use std::result::Result;
fn read(input: &mut String) {
stdout().flush().expect("failed to flush");
stdin().read_line(input).expect("failed to read");
}
fn main() {
loop {
let mut number = String::new();
let rand_ = rand::thread_rng().gen_range(1..10);
let mut killcheck = String::new();
println!("Input a number between 0 and 10 \n");
match rand_ {
1..=5 => println!("it is 1 through 5"),
5..=10 => println!("it is 5 through 10"),
_ => continue,
}
read(&mut number);
let number: i16 = number.trim().parse::<i16>().unwrap();
match number {
Ok(ok) => continue,
Err(e) => println!("No number could be found"),
_ => continue
}
read(&mut killcheck);
if killcheck.trim() == "end" {
println!("error is null");
process::exit(0x0100);
}
if number == rand_ {
println!("Currect!\n")
}
if number != rand_ {
println!("Incorrect!\n")
}
read(&mut killcheck);
if killcheck.trim() == "end" {
println!("error is null");
process::exit(0x0100);
}
}
}
I don't understand the problem, I would think it would just be checking if it can happen then continue.
let number: i16 = number.trim().parse::<i16>().unwrap(); //*number is i16 type with the use of unwrap() instead of Result*
match number {
Ok(ok) => continue, //*It should be some i16 numbers here instead of Result(Ok/Err)*
Err(e) => println!("No number could be found"),
_ => continue
}

Why is my code failling in Rust when I don't declare a variable in the loop

Code that fails
use std::cmp::Ordering;
use std::io;
use rand::Rng;
fn main() {
println!("Guess the Number!");
let mut guess = String::new();
let secret_number = rand::thread_rng().gen_range(1..10);
loop {
println!("Please input the guess.");
io::stdin().read_line(&mut guess).expect("Failed to read line");
let guess: u32 = guess.trim().parse().expect("Please Type a Number");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too Small!"),
Ordering::Greater => println!("Too Large!"),
Ordering::Equal => println!("You got this"),
}
}
}
Code that works
use std::cmp::Ordering;
use std::io;
use rand::Rng;
fn main() {
println!("Guess the Number!");
let secret_number = rand::thread_rng().gen_range(1..10);
loop {
println!("Please input the guess.");
//Now Defining guess Inside the loop
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Failed to read line");
let guess: u32 = guess.trim().parse().expect("Please Type a Number");
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too Small!"),
Ordering::Greater => println!("Too Large!"),
Ordering::Equal => println!("You got this"),
}
}
}
From what I can understand, defining guess each time the loop runs makes my code work, otherwise, I get the error shown below
Why does this happen, I am entering integers only so I should not get a data type mismatch, and why does it work as soon as I start defining guess, again and again, I think this has something to do with how rust handle spaces or newline in input, but not sure.
Any help would be appreciated
Error
It failed because each time when you read_line it will append the stdin buffer to the guess string. So guess will change as following.
1\n -> 1\n2\n -> 1\n2\n3\n
To fix this issue, clear the guess variable in each loop.
use std::cmp::Ordering;
use std::io;
use rand::Rng;
fn main() {
println!("Guess the Number!");
let mut guess = String::new();
let secret_number = rand::thread_rng().gen_range(1..10);
loop {
println!("Please input the guess.");
io::stdin().read_line(&mut guess).expect("Failed to read line");
// I renamed it to a different name. Because I want to access the guess string in the end of the loop
let guess_nmb: u32 = guess.trim().parse().expect("Please Type a Number");
println!("You guessed: {}", guess_nmb);
match guess_nmb.cmp(&secret_number) {
Ordering::Less => println!("Too Small!"),
Ordering::Greater => println!("Too Large!"),
Ordering::Equal => println!("You got this"),
}
// Clear the guess string
guess.clear();
}
}

How to check for EOF in read_line in Rust 1.12?

Consider the following program, how do I detect EOF in stdin and break the loop?
use std::io;
use std::process;
fn main() {
let mut sum = 0;
loop {
let mut number_str = String::new();
match io::stdin().read_line(&mut number_str) {
Ok(n) => {},
Err(e) => { println!("ERROR: got '{}' when reading a line", e) }
}
match number_str.trim().parse::<i32>() {
Err(n) => {
println!("ERROR: Entered something that is not a number: '{}'",
number_str.trim_right());
process::exit(1)
},
Ok(n) => { sum += n }
}
}
}
Note: there is an identical question but the answer seems to be out of date anymore, which is why I added a version number in the question title.
From the documentation for read_line:
If this reader is currently at EOF then this function will not modify buf and will return Ok(n) where n is the number of bytes which were read.

Guessing game, error on shadowing guess bind

I'm following the Rust tutorial but I'm stuck on this code (the last snippet in the page):
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng;
fn main() {
println!("Guess the number!");
let secret_number = rand::thread_rng().gen_range(1, 101);
println!("The secret number is {}", secret_number);
loop {
println!("Please input your guess");
let mut guess = String::new();
io::stdin()
.read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
}
println!("You guessed: {}", guess);
match guess.cmp(&secret_number) {
Ordering::Less => println!("Too small!"),
Ordering::Greater => println!("Too big!"),
Ordering::Equal => {
println!("You win!");
break;
}
}
}
}
When I run cargo run I have the following error:
src/main.rs:23:47: 23:48 error: expected one of `.`, `;`, or an operator, found `{`
src/main.rs:23 let guess: u32 = guess.trim().parse() {
^
What's the right syntax?
There is a syntax error and the compiler message is directing your attention to the wrong place on the line to fix the problem.
The parse method evaluates to a value. This expression should not be followed by a block, causing the syntax error reported by the compiler.
https://doc.rust-lang.org/std/string/struct.String.html#method.parse
The example you linked to has the keyword match between the assignment and call to parse. The match keyword takes an expression and branches based on the value of the expression. The block contains the branching patterns and expressions. In this case it is also destructuring the Result into either an u32 or u32::Err.
https://doc.rust-lang.org/book/match.html
Below is an example that separates the parse and match for clarity.
// Store result of parsing in a variable
let parse_result = guess.trim().parse();
// Destructure the result
let guess: u32 = match parse_result {
// If parse succeeded evaluate to the number
Ok(num) => num,
// If parse failed repeat the loop
Err(_) => continue,
};
You forgot to add the match keyword before calling guess.trim().parse()
That line should look like this:
let guess : u32 = match guess.trim().parse() {...
source: https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

How to check for EOF with `read_line()`?

Given the code below, how can I specifically check for EOF? Or rather, how can I distinguish between "there's nothing here" and "it exploded"?
match io::stdin().read_line() {
Ok(l) => print!("{}", l),
Err(_) => do_something_else(),
}
From the documentation for read_line:
If successful, this function will return the total number of bytes read.
If this function returns Ok(0), the stream has reached EOF.
This means we can check for a successful value of zero:
use std::io::{self, BufRead};
fn main() -> io::Result<()> {
let mut empty: &[u8] = &[];
let mut buffer = String::new();
let bytes = empty.read_line(&mut buffer)?;
if bytes == 0 {
println!("EOF reached");
}
Ok(())
}

Resources