SleepyMode
Coder
Code in this thread will be formatted as C# because it's easier to read than having unhighlighted c ode, but it's all written in Rust, which is not available as a formatting option.
There's an issue I've been repeatedly running into with Rust when creating CLI applications, for example, the most familiar Guessing Game from the tutorial (although fairly modified to fit the scenario):
C#:
use std::io;
use rand::Rng;
use std::io::Write;
use std::cmp::Ordering;
fn main() {
println!("[+] Guessing Game:");
println!("[+] A random number will be selected between 0 to 100 (inclusive).");
println!("[+] You'll be asked to enter numbers until you enter the correct number.");
println!("[+] The game will support you by telling you which direction your next game should be.");
println!();
let target: u32 = rand::thread_rng().gen_range(0..=100);
let mut guess_raw = String::new();
loop {
print!("[+] Enter your guess: ");
io::stdout().flush().unwrap();
guess_raw.clear();
io::stdin().read_line(&mut guess_raw).expect("Failed to read line.");
let guess: u32;
match guess_raw.trim().parse() {
Ok(parsed_number) => {
guess = parsed_number;
},
Err(_error) => {
print!(" [NOT A NUMBER]");
io::stdout().flush().unwrap();
continue;
}
}
match guess.cmp(&target) {
Ordering::Less => {
print!(" [TOO SMALL]");
io::stdout().flush().unwrap();
continue;
},
Ordering::Greater => {
print!(" [TOO BIG]");
io::stdout().flush().unwrap();
continue;
},
Ordering::Equal => {
print!(" [CORRECT]");
io::stdout().flush().unwrap();
break;
}
}
}
println!();
println!("[+] You've won the game!");
}
Well then, with this in mind, looking at the code gives you the thought it would look a bit like this:
Code:
[+] Guessing Game:
[+] A random number will be selected between 0 to 100 (inclusive).
[+] You'll be asked to enter numbers until you enter the correct number.
[+] The game will support you by telling you which direction your next game should be.
[+] Enter your guess: 10 [TOO SMALL]
[+] Enter your guess: 50 [TOO SMALL]
[+] Enter your guess: 80 [TOO SMALL]
[+] Enter your guess: 90 [TOO SMALL]
[+] Enter your guess: 95 [TOO SMALL]
[+] Enter your guess: 100 [TOO BIG]
[+] Enter your guess: 98 [TOO BIG]
[+] Enter your guess: 97 [CORRECT]
[+] You've won the game!
Process finished with exit code 0
But this is what it actually looks like:
Code:
[+] Guessing Game:
[+] A random number will be selected between 0 to 100 (inclusive).
[+] You'll be asked to enter numbers until you enter the correct number.
[+] The game will support you by telling you which direction your next game should be.
[+] Enter your guess: 50
[TOO BIG][+] Enter your guess: 30
[TOO SMALL][+] Enter your guess: 40
[TOO SMALL][+] Enter your guess: 45
[TOO SMALL][+] Enter your guess: 48
[CORRECT]
[+] You've won the game!
Process finished with exit code 0
And after some research I've reached the conclusion that the issue is with the following line:
io::stdin().read_line(&mut guess_raw).expect("Failed to read line.");
Particularly, io::stsdin().read_line(), which calls the following (eventually):
unsafe { append_to_string(buf, |b| read_until(self, b'\n', b)) }
The above call is what I'm pretty sure is the cause for the entire issue, but I've yet to find any reasonable way to work around it. This problem came up a lot in places like Reddit but only in the sense that the result of the read included the newline character, in which case it can easily just be trimmed, but that doesn't allow you to write in-line after text has been read from stdin.
Does anyone have a clue on a way to read a line in Rust without having it skip to the next line?