-
Notifications
You must be signed in to change notification settings - Fork 13.4k
Guide: Standard input #15534
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Guide: Standard input #15534
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -966,6 +966,112 @@ loop | |
|
||
break/continue | ||
|
||
## Standard Input | ||
|
||
Getting input from the keyboard is pretty easy, but uses some things | ||
we haven't seen before. Here's a simple program that reads some input, | ||
and then prints it back out: | ||
|
||
```{rust,ignore} | ||
fn main() { | ||
println!("Type something!"); | ||
|
||
let input = std::io::stdin().read_line().ok().expect("Failed to read line"); | ||
|
||
println!("{}", input); | ||
} | ||
``` | ||
|
||
Let's go over these chunks, one by one: | ||
|
||
```{rust} | ||
std::io::stdin(); | ||
``` | ||
|
||
This calls a function, `stdin()`, that lives inside the `std::io` module. As | ||
you can imagine, everything in `std` is provided by Rust, the 'standard | ||
library.' We'll talk more about the module system later. | ||
|
||
Since writing the fully qualified name all the time is annoying, we can use | ||
the `use` statement to import it in: | ||
|
||
```{rust} | ||
use std::io::stdin; | ||
|
||
stdin(); | ||
``` | ||
|
||
However, it's considered better practice to not import individual functions, but | ||
to import the module, and only use one level of qualification: | ||
|
||
```{rust} | ||
use std::io; | ||
|
||
io::stdin(); | ||
``` | ||
|
||
Let's update our example to use this style: | ||
|
||
```{rust,ignore} | ||
use std::io; | ||
|
||
fn main() { | ||
println!("Type something!"); | ||
|
||
let input = io::stdin().read_line().ok().expect("Failed to read line"); | ||
|
||
println!("{}", input); | ||
} | ||
``` | ||
|
||
Next up: | ||
|
||
```{rust,ignore} | ||
.read_line() | ||
``` | ||
|
||
The `read_line()` method can be called on the result of `stdin()` to return | ||
a full line of input. Nice and easy. | ||
|
||
```{rust,ignore} | ||
.ok().expect("Failed to read line"); | ||
``` | ||
|
||
Here's the thing: reading a line from standard input could fail. For example, | ||
if this program isn't running in a terminal, but is running as part of a cron | ||
job, or some other context where there's no standard input. So Rust expects us | ||
to handle this case. Given that we plan on always running this program in a | ||
terminal, we use the `ok()` method to tell Rust that we're expecting everything | ||
to be just peachy, and the `expect()` method on that result to give an error | ||
message if our expectation goes wrong. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I know you're trying to keep it simple until later but I think a few more details here would be helpful. I thought this was puzzling. Too few details and the user will modify something and confuse themselves without recourse.
What you mean by this is unclear. Rust expected us to handle it; what if we don't? Will it crash? Will Rust refuse compilation? Why tell it Also, see below: // I'll remove the error handling to see what happens...
// Should this compile because some case wasn't handled? Apparently it does...
let input = io::stdin().read_line(); // `Ok(test)` instead of `test`...
let input = io::stdin().read_line().ok(); // `Some(test)`...
// Rereads explanation and is more confused about `Ok` and `Some` Here's a different type of explanation (hopefully sorta accurate):
Not advocating my wording. I just wanted to give a possible example so it wasn't just a complaint. [EDIT] Moved comment to diff instead of on file There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right. There's a balance that needs struck here: this stuff requires concepts we don't have yet. I don't want to have to fully explain There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another option is to explicitly defer it: // Cheap error handling which is quite complicated and
// will not be explained until Section 7.4
// Basically, use normally and call "Error!" when it errors
.ok().expect("Error!"); Accelerated C++ does that often and I find it really helpful. They never lie, they explain enough for the section, and they tell you where to look if you want more info (will be easier to link when the guides finished though...). Accelerated C++ is quite terse and dry probably though. Examples:
|
||
|
||
We will cover the exact details of how all of this works later in the Guide. | ||
For now, this is all you need. | ||
|
||
With long lines like this, Rust gives you some flexibility with the whitespace. | ||
We _could_ write the example like this: | ||
|
||
```{rust,ignore} | ||
use std::io; | ||
|
||
fn main() { | ||
println!("Type something!"); | ||
|
||
let input = io::stdin() | ||
.read_line() | ||
.ok() | ||
.expect("Failed to read line"); | ||
|
||
println!("{}", input); | ||
} | ||
``` | ||
|
||
Sometimes, this makes things more readable. Sometimes, less. Use your judgement | ||
here. | ||
|
||
That's all you need to get basic input from the standard input! It's not too | ||
complicated, but there are a number of small parts. | ||
|
||
## Guessing Game: complete | ||
|
||
At this point, you have successfully built the Guessing Game! Congratulations! | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, I don't personally regard
expect
andunwrap
as something we should be encouraging for legitimate errors: in my mind they are essentially assertions that theNone
case never happens, sincefail!
isn't really recoverable at all.I would prefer
Of course, this is more verbose, may introduce too much at a time...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a big call for a bikeshed, and we all ended up with https://github.com/steveklabnik/guessing_game/blob/master/src/guessing_game.rs , which is what I'm building to.
I kinda agree with you, but not being able to read a line from stdin is a pretty exceptional case in this situation, and the entire program is useless in that case, therefore,
fail
ing makes sense.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really like that
.expect
/.unwrap
is the first example of error handling. :(But... doing it properly is certainly more ugly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, that code actually handles EOF poorly:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Control-D to quit is poor? ;)
But yes, it could be better. The idea here is to just do the basics, not to get it absolutely perfect. There's a balance here, you know?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some improvements in mind here, so let's just merge this for now, and then we can improve in this specific place.