In the book, rustaceans-to-be get to build a guessing game. In this guessing game, there's the following snippet:
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
Um... why would read_line() fail, I thought. And then found out the hard way: Instead of 50 I entered 5ß (German keyboards...) and... read line failed.
So I thought I'd fix it quickly.
match io::stdin().read_line(&mut guess) {
Ok(str) => str,
Err(_) => println!("Please only enter ASCII characters.");
}
That returned a type mismatch: Expected (), found usize
Ah, right! read_line() returns the number of entered bytes in the Ok result. But I don't want to do anything with that information so I replaced the Ok statement from above:
Ok(_) => {},
That works. But is this the right way to do it? I'm basically telling the program to run an empty code block on Ok, which I'd consider bad style in languages like Java, PHP, JavaScript etc.
Since you're only interested in one of the match arms, you can use an if let binding:
if let Err (_) = io::stdin().read_line(&mut guess) {
println!("Please only enter ASCII characters.");
}
Related
In Rust, is it possible to chain more functions after ok, unwrap or expect?
Given code:
stdin()
.read_line(&mut guess)
.ok()
.expect("Couldn't read line!");
If I want to trim the string, is there a syntax like this?
stdin()
.read_line(&mut guess)
.ok().trim()
.expect("Couldn't read line!");
I know using match I could do it https://stackoverflow.com/a/56264723/15504324
In this case the problem is that, you cannot, because the buffer is guess, readline would return the bytes read
Anyway:
You can use map to operate the value in case there is any Before unwraping/expecting:
Ok("Foo bar ")
.ok()
.map(str::trim)
.expect("Couldn't read line!");
After it you can use the type already, so just chain the call.
Ok("Foo bar ")
.unwrap()
.trim();
I have not yet used match so I can't understand it very well. I want to try to write a number in the console, but I got the wrong value from match.
My code:
let mut choice = String::new();
let choice = io::stdin().read_line(&mut choice).unwrap();
match choice {
1 => println!("1"),
2 => println!("2"),
3 => println!("3"),
_ => println!("this number doesn't exist"),
};
And the printed value is 2, but I wrote 1 in the console.
read_line doesn't return your input; it returns the number of bytes entered (including the newline at the end). You entered 1 followed by a newline, so 2 characters, so you get 2 as the return value. The actual text is stored in the string you passed a mutable reference to.
let mut choice = String::new();
// Modifies the choice variable in-place.
io::stdin().read_line(&mut choice).unwrap();
// Now choice contains our input, so let's parse it as an integer.
let choice = i32::from_str(&choice).unwrap();
match choice {
...
}
I am studying Rust and upon working on the Guessing Game I found this odd behaviour:
use std::io;
fn main() {
println!("Welcome!");
let mut input = String::new();
print!("Please type something:"); // this line is not printed UNTIL the Enter key is pressed
io::stdin()
.read_line(&mut input)
.expect("Failed to read input!");
println!("Bye!");
}
The following happens:
Welcome! is printed
Please type something: is NOT printed
If you type some text and press Enter, you will see your text followed by Please type something:Bye!
How can I print a message to the standard output and have the input being printed on the same line?
For instance:
Please enter your name:
(user types Chuck Norris)
Please enter your name: Chuck Norris
From the docs for std::print:
Note that stdout is frequently line-buffered by default so it may be necessary to use io::stdout().flush() to ensure the output is emitted immediately.
So looks like you need to call io::stdout().flush().
I am learning Rust from The Book and I just finished the first exercise, the guessing game.
I use cargo to build and run my little crate.
$ cargo --version
cargo 1.37.0 (9edd08916 2019-08-02)
$ rustc --version
rustc 1.37.0 (eae3437df 2019-08-13)
Everything run fine, including release mode. Nevertheless, I do not understand a the following behaviour of Rust: I have to redeclare the variable which contains the user input at each iteration of the loop.
Since the exercise is guided step-by-step, my code is the same that the one from the book. Nevertheless, the code from the book is the following:
loop {
// Some code to display instructions.
// Reallocate a new string at each iteration!
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
// Some code to check if the player found the secret number.
}
Noticing this systematic reallocation, I moved the string declaration outside the loop:
// Allocate the string once.
let mut guess = String::new();
loop {
// Some code to display instructions.
io::stdin().read_line(&mut guess)
.expect("Failed to read line");
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
// Some code to check if the player found the secret number.
}
However, Rust did not appreciate this: at the second iteration of the loop, it panics every time.
Why can't I reuse the same mutable variable more than once? Do I not understand something?
EDIT: read_line does not clear the content of the previous input, but appends to it the following one.
Let's say the player enters 1 then 2, the final value of guess will be "1\n2\n".
However, trim() removes the "blank" characters at the beginning and the end of the string, leaving a \n in the middle: parse() panics!
Your code as-is compiles and runs fine on my setup (same version of rust). The panic must happen in the commented-out part of your code. Some comments, though: the scoping in your loop is tricky: guess at the top-half of the loop is the string declared outside the loop, and is the parsed integer in the second half.
More importantly, multiple calls to read_line being passed the same string appends to the string, which probably isn't your intention given the way you're parsing the string. Sprinkling in println!'s of your guess variables should be illuminating. Your code will probably be fixed if you add a guess.clear() on the string after you've parsed the number, but to do that, you'll probably want to rename the u32 guess.
As an aside, you might consider using a BufReader and the for line in reader.lines()) pattern described here.
The macro println! in Rust always leaves a newline character at the end of each output. For example
println!("Enter the number : ");
io::stdin().read_line(&mut num);
gives the output
Enter the number :
56
I don't want the user's input 56 to be on a new line. How do I do this?
It's trickier than it would seem at first glance. Other answers mention the print! macro but it's not quite that simple. You'll likely need to flush stdout, as it may not be written to the screen immediately. flush() is a trait method that is part of std::io::Write so that needs to be in scope for it to work (this is a pretty easy early mistake).
use std::io;
use std::io::Write; // <--- bring flush() into scope
fn main() {
println!("I'm picking a number between 1 and 100...");
print!("Enter a number: ");
io::stdout().flush().unwrap();
let mut val = String::new();
io::stdin().read_line(&mut val)
.expect("Error getting guess");
println!("You entered {}", val);
}
You can use the print! macro instead.
print!("Enter the number : ");
io::stdin().read_line(&mut num);
Beware:
Note that stdout is frequently line-buffered by default so it may be necessary to use io::stdout().flush() to ensure the output is emitted immediately.
Don't use the print/ln!-macros. Use write/ln!-macros.
It is more verbose, but print/ln! are problematic for using in command-line apps where their output might get piped or redirected to other apps, which is characteristic for Unix environments.
There is used always the same (only once requested and "buffered") stdout-device, but the stdout-device of the system is changed for piping/redirecting. So for each output to stdout you have to request the current stdout-device (std::io::stdout()). This can be done with write/ln!-macros.
So to say print/ln! is broken and there is an open issue since years.