I'm having some problems with some basic I/O stuff. Specifically, the text "Please enter your name" is written to the output after I type in my name and hit Enter:
use std::io;
fn main() {
print!("Please enter your name: ");
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => println!(""),
Err(err) => println!("Could not parse input: {}", err)
}
println!("Hello, {}!", name.trim());
}
gives the following output:
Compiling chat v0.1.0 (file:///home/marcus/dev/rust/chat)
Running `target/debug/chat`
marcus
Please enter your name:
Hello, marcus!
Where the first "marcus" was entered by me. Why won't the program print "Please enter your name" before waiting for input?
Is it possible to "do nothing" if a returned Result is Ok? In the example, Ok() means that I have saved the input in the variable name. That's great. But what do I do with Ok() => in this case?
Why won't the program print "Please enter your name" before waiting for input?
Well, it did. It's just that, for performance reasons, standard output is buffered. The write completed, but it was only writing to memory. If you want it to actually display to the user, you have to trigger a flush. This can be done either by writing a newline, or by doing it explicitly:
io::Write::flush(&mut io::stdout()).expect("flush failed!");
// If you "use" `io::Write`...
io::stdout().flush().expect("flush failed!");
Also, is it possible to "do nothing" if a returned Result is Ok?
Sure. Just... do nothing.
match io::stdin().read_line(&mut name) {
Ok(_) => { /* nothing */ },
Err(err) => println!("Could not parse input: {}", err)
}
The relevant requirement here is that all arms in a match have to have the same result type. In the case of println!, it results in a (); aside from an empty block (or another function that returns ()), you can just use a literal:
match io::stdin().read_line(&mut name) {
Ok(_) => (),
Err(err) => println!("Could not parse input: {}", err)
}
This is explained on the documentation for print!. Since print! does not emit a newline and stdout is line-buffered, you won't see any output. You can manually flush stdout:
use std::io::{self, Write};
print!("Please enter your name: ");
io::stdout().flush();
For your second question you can always return unit explicitly:
Ok(_) => (),
So your program becomes:
use std::io::{self, Write};
fn main() {
print!("Please enter your name: ");
io::stdout().flush();
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => (),
Err(err) => println!("Could not parse input: {}", err)
}
println!("Hello, {}!", name.trim());
}
As #Veedrac pointed out in their (now deleted) comment, you can use an if let expression in place of the match on the result of read_line:
if let Err(err) = io::stdin().read_line(&mut name) {
println!("Could not parse input: {}", err)
}
use std::io::{self, Write};
fn main() {
print!("Please enter your name: ");
let _ = io::stdout().flush();
// your io code
}
Related
In Rust, I want to remove a file. The documentation tells me that fs::remove_file returns an error if the file is a directory, does not exist or the user lacks the permissions for the action.
Now, in my scenario, I would like to distinguish between those three cases. However, my debugger doesn't show me the type of the error and a println! results in simply printing the error message.
So, how could I distinguish between those cases?
use std::fs;
fn rem(path: &str) {
let msg = match fs::remove_file(path) {
Ok(()) => "is fine",
Err(NotExist) => "does not exist", // TODO: Pattern
Err(IsDir) => "is a directory", // TODO: Pattern
Err(_) => "is not yours",
};
println!("The file {}!", msg);
}
fn main() {
rem("/tmp/this-file-hopefully-not-exists.xyz"); // print "not exist"
rem("/tmp/this-is-a-directory"); // prints "is a directory"
rem("/tmp/this-file-belongs-to-root.xyz"); // prints "is not yours"
}
std::fs::remove_file returns a std::io::Result<()>, which is just an alias for Result<(), std::io::Error>.
So you can match on this to extract the type of the error message. In particular, you're probably going to want to look at the .kind() of the error:
fn rem(path: &str) {
use std::io::ErrorKind;
let message = match std::fs::remove_file(path) {
Ok(()) => "ok",
Err(e) if e.kind() == ErrorKind::NotFound => "not found",
Err(e) if e.kind() == ErrorKind::IsADirectory => "is a directory",
Err(e) => "other",
}
println!("{message}");
}
Ignoring the fact that I'm using unwrap to ignore what happens if the file doesn't exist, it seems to me like this short bit of code should work (as long as the file does exist):
use std::fs::File;
use std::io::Write;
fn main() {
let mut f = File::open("test.txt").unwrap();
let result = f.write_all(b"some data");
match result {
Ok(_) => println!("Data written successfully"),
Err(e) => panic!("Failed to write data: {}", {e}),
}
}
Instead, I'm getting this:
thread 'main' panicked at 'Failed to write data: Bad file descriptor (os error 9)', src/main.rs:10:19
To be clear, I know if I follow one of the many examples online, I can write to a file. The question isn't "how do I write to a file?". It's why THIS isn't working.
It isn't working because File::open() open's a file in read-only mode. Instead you have to use File::create() which opens a file in write-only mode. Alternatively you can also use OpenOptions, to further specify if you want to append() to a file instead.
use std::fs::File;
use std::io::Write;
fn main() {
let mut f = File::create("test.txt").unwrap();
let result = f.write_all(b"some data");
match result {
Ok(_) => println!("Data written successfully"),
Err(err) => panic!("Failed to write data: {}", err),
}
}
Using File::create() is the same as using OpenOptions in the following way:
use std::fs::OpenOptions;
use std::io::Write;
fn main() {
let mut f = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open("test.txt")
.unwrap();
let result = f.write_all(b"some data");
match result {
Ok(_) => println!("Data written successfully"),
Err(err) => panic!("Failed to write data: {}", err),
}
}
I'm having some problems with some basic I/O stuff. Specifically, the text "Please enter your name" is written to the output after I type in my name and hit Enter:
use std::io;
fn main() {
print!("Please enter your name: ");
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => println!(""),
Err(err) => println!("Could not parse input: {}", err)
}
println!("Hello, {}!", name.trim());
}
gives the following output:
Compiling chat v0.1.0 (file:///home/marcus/dev/rust/chat)
Running `target/debug/chat`
marcus
Please enter your name:
Hello, marcus!
Where the first "marcus" was entered by me. Why won't the program print "Please enter your name" before waiting for input?
Is it possible to "do nothing" if a returned Result is Ok? In the example, Ok() means that I have saved the input in the variable name. That's great. But what do I do with Ok() => in this case?
Why won't the program print "Please enter your name" before waiting for input?
Well, it did. It's just that, for performance reasons, standard output is buffered. The write completed, but it was only writing to memory. If you want it to actually display to the user, you have to trigger a flush. This can be done either by writing a newline, or by doing it explicitly:
io::Write::flush(&mut io::stdout()).expect("flush failed!");
// If you "use" `io::Write`...
io::stdout().flush().expect("flush failed!");
Also, is it possible to "do nothing" if a returned Result is Ok?
Sure. Just... do nothing.
match io::stdin().read_line(&mut name) {
Ok(_) => { /* nothing */ },
Err(err) => println!("Could not parse input: {}", err)
}
The relevant requirement here is that all arms in a match have to have the same result type. In the case of println!, it results in a (); aside from an empty block (or another function that returns ()), you can just use a literal:
match io::stdin().read_line(&mut name) {
Ok(_) => (),
Err(err) => println!("Could not parse input: {}", err)
}
This is explained on the documentation for print!. Since print! does not emit a newline and stdout is line-buffered, you won't see any output. You can manually flush stdout:
use std::io::{self, Write};
print!("Please enter your name: ");
io::stdout().flush();
For your second question you can always return unit explicitly:
Ok(_) => (),
So your program becomes:
use std::io::{self, Write};
fn main() {
print!("Please enter your name: ");
io::stdout().flush();
let mut name = String::new();
match io::stdin().read_line(&mut name) {
Ok(_) => (),
Err(err) => println!("Could not parse input: {}", err)
}
println!("Hello, {}!", name.trim());
}
As #Veedrac pointed out in their (now deleted) comment, you can use an if let expression in place of the match on the result of read_line:
if let Err(err) = io::stdin().read_line(&mut name) {
println!("Could not parse input: {}", err)
}
use std::io::{self, Write};
fn main() {
print!("Please enter your name: ");
let _ = io::stdout().flush();
// your io code
}
So I'm extending the guessing game to basically ask the user if they want to play again. If they type y, the program will return to the main game loop and if they type n, it just breaks out of the current loop and the program ends. If they type anything else, in theory it should just jump to the top of the play_again loop and reassign the yae_or_nay variable as whatever the player inputs next. But it doesn't do that, or at least it looks like it overwrites it incorrectly. Am I reassigning the variable incorrectly? Heres the code (note that the first half of the program is almost the same as in the textbook, but since the program is so short I just decided to include the whole thing):
extern crate rand;
use std::io;
use std::cmp::Ordering;
use rand::Rng; // random number generation library from rand
fn main() {
println!("Guessing game!\n");
let mut answer = rand::thread_rng().gen_range(1,101);
'gameloop: loop {
println!("Please print your guess:");
let mut yae_or_nay = String::new();
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(_) => {
println!("Not a number!");
continue;
}
};
match guess.cmp(&answer) {
Ordering::Less => println!("Higher!"),
Ordering::Greater => println!("Lower!"),
Ordering::Equal => {
println!("Correct! Would you like to play again? (y/n)");
'play_again: loop {
io::stdin().read_line(&mut yae_or_nay)
.expect("Failed to read line.");
match yae_or_nay.trim() { // match against a string
"y" => {
answer = rand::thread_rng().gen_range(1,101);
println!("Playing again...");
continue 'gameloop;
},
"n" => {
println!("Thanks for playing! Exiting now.");
break
},
_ => {
println!("what? You entered {}", &yae_or_nay);
continue 'play_again
}
};
}
}
}
}
}
And here's a snippet of the console output:
46
Correct! Would you like to play again? (y/n)
i
what? You entered i
y
what? You entered i
y
n
what? You entered i
y
n
. // period here for formatting sake, not actually in console
As you can see, the way my program is reassigning yae_or_nay seems pretty strange. Anyone know what's going on? Thanks in advance for any help.
The issue is that you never clear yae_or_nay, and io::stdin().read_line(&mut yae_or_nay) will append to the string, not replace its content.
Read all bytes until a newline (the 0xA byte) is reached, and append them to the provided buffer.
Either you should do
yae_or_nay.clear();
before you read into it, or likely better would be to move the declaration to
'play_again: loop {
let mut yae_or_nay = String::new();
io::stdin().read_line(&mut yae_or_nay)
.expect("Failed to read line.");
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