Skip to content
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

Draft changes for eprintln. #615

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions second-edition/src/ch01-02-hello-world.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,15 +88,24 @@ requires these around all function bodies. It's considered good style to put
the opening curly brace on the same line as the function declaration, with one
space in between.

If you're familiar with languages like C, you may be wondering why `main`
doesn't return a value. We'll talk about that when we talk about error
handling, in Chapter 9.3.

Inside the `main` function:

```rust
println!("Hello, world!");
```

This line does all of the work in this little program: it prints text to the
screen. There are a number of details to notice here. The first is that Rust
style is to indent with four spaces, not a tab.
screen. (Technically, it prints text on the _standard output stream_, which
is displayed on the screen by your terminal window. You may be familiar with
standard output already from other programming languages; if not, we will
talk about it more in Chapters 9.3 and 12.6.)

There are a number of details to notice here. The first is that Rust style is
to indent with four spaces, not a tab.

The second important part is `println!`. This is calling a Rust *macro*,
which is how metaprogramming is done in Rust. If it were calling a function
Expand All @@ -106,7 +115,9 @@ that when you see a `!` that means that you’re calling a macro instead of a
normal function.

Next is `"Hello, world!"` which is a *string*. We pass this string as an
argument to `println!`, which prints the string to the screen. Easy enough!
argument to `println!`, which prints it. Easy enough! `println!` can print
other things as well as strings; you'll see more ways to use it as we go on.


The line ends with a semicolon (`;`). The `;` indicates that this expression is
over, and the next one is ready to begin. Most lines of Rust code end with a
Expand Down
22 changes: 13 additions & 9 deletions second-edition/src/ch02-00-guessing-game-tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,10 @@ src/main.rs:10 io::stdin().read_line(&mut guess);
```

Rust warns that we haven’t used the `Result` value returned from `read_line`,
indicating that the program hasn’t handled a possible error. The right way to
suppress the warning is to actually write error handling, but since we just
want to crash this program when a problem occurs, we can use `expect`. You’ll
indicating that the program hasn’t handled a possible error.

Normally you don’t want your program to crash just because it failed to read
some input, but since we’re just getting started, it’s OK for now. You’ll
learn about recovering from errors in Chapter 9.

### Printing Values with `println!` Placeholders
Expand All @@ -304,11 +305,13 @@ the code added so far, which is the following:
println!("You guessed: {}", guess);
```

This line prints out the string we saved the user’s input in. The set of `{}`
is a placeholder that holds a value in place. You can print more than one value
using `{}`: the first set of `{}` holds the first value listed after the format
string, the second set holds the second value, and so on. Printing out multiple
values in one call to `println!` would look like this:
This prints the string we read on the previous line, with `You guessed: ` in
front. We are now giving `println!` two arguments. The first argument is
called the _format string_, and the second argument is a _value_ to print.
When used this way, `println!` prints the text of the format string, but where
the curly braces `{}` appear, it substitutes the value. `{}` is called a
_placeholder_ for the value. You can use more than one `{}`, along with more
than one value:

```rust
let x = 5;
Expand All @@ -317,7 +320,8 @@ let y = 10;
println!("x = {} and y = {}", x, y);
```

This code would print out `x = 5 and y = 10`.
This code would print out `x = 5 and y = 10`. Notice that you can print
numbers this way, as well as strings.

### Testing the First Part

Expand Down
2 changes: 1 addition & 1 deletion second-edition/src/ch06-02-match.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ values that match the pattern. This is how we can extract values out of enum
variants.

As an example, let’s change one of our enum variants to hold data inside it.
From 1999 through 2008, the United States printed quarters with different
From 1999 through 2008, the United States minted quarters with different
designs for each of the 50 states on one side. No other coins got state
designs, so only quarters have this extra value. We can add this information to
our `enum` by changing the `Quarter` variant to include a `State` value stored
Expand Down
8 changes: 4 additions & 4 deletions second-edition/src/ch09-02-recoverable-errors-with-result.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ fn main() {
let f = match f {
Ok(file) => file,
Err(error) => {
panic!("There was a problem opening the file: {:?}", error)
panic!("There was a problem opening 'hello.txt': {:?}", error)
},
};
}
Expand All @@ -127,7 +127,7 @@ there’s no file named *hello.txt* in our current directory and we run this
code, we’ll see the following output from the `panic!` macro:

```text
thread 'main' panicked at 'There was a problem opening the file: Error { repr:
thread 'main' panicked at 'There was a problem opening 'hello.txt': Error { repr:
Os { code: 2, message: "No such file or directory" } }', src/main.rs:8
```

Expand Down Expand Up @@ -158,15 +158,15 @@ fn main() {
Ok(fc) => fc,
Err(e) => {
panic!(
"Tried to create file but there was a problem: {:?}",
"Tried to create 'hello.txt' but there was a problem: {:?}",
e
)
},
}
},
Err(error) => {
panic!(
"There was a problem opening the file: {:?}",
"There was a problem opening 'hello.txt': {:?}",
error
)
},
Expand Down
179 changes: 110 additions & 69 deletions second-edition/src/ch09-03-to-panic-or-not-to-panic.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,119 @@
## To `panic!` or Not To `panic!`

So how do you decide when you should `panic!` and when you should return
`Result`? When code panics, there’s no way to recover. You could choose to call
`panic!` for any error situation, whether there’s a possible way to recover or
not, but then you’re making the decision for your callers that a situation is
unrecoverable. When you choose to return a `Result` value, you give your caller
options, rather than making the decision for them. They could choose to attempt
to recover in a way that’s appropriate for their situation, or they could
decide that actually, an `Err` value in this case is unrecoverable, so they can
call `panic!` and turn your recoverable error into an unrecoverable one.
`Result`? As a general rule, we recommend you only use `panic!` for situations
where the error indicates a bug in the program. For instance, library
functions often have *contracts*: their inputs must meet inputs particular
requirements, and if they don't, the calling code has a bug. Panicking when
the contract is violated makes sense because a contract violation always
indicates a bug in the program, and trying to recover may only make matters
worse. This is the main reason that the standard library will `panic!` if you
attempt an out-of-bounds array access: trying to access memory that doesn’t
belong to the current data structure is a common security problem. Contracts
for a function, especially when a violation will cause a panic, should be
explained in the API documentation for the function.

If you design your API to take good advantage of Rust's type system, you will
often find that contract checking is unnecessary, because violations cannot
happen. If your function has a particular type as a parameter, you can proceed
with your code’s logic knowing that the compiler has already ensured you have
a valid value. For example, if you have a type rather than an `Option`, your
program expects to have *something* rather than *nothing*. Your code then
doesn’t have to handle two cases for the `Some` and `None` variants, it will
only have one case for definitely having a value. Code trying to pass nothing
to your function won’t even compile, so your function doesn’t have to check
for that case at runtime. Another example is using an unsigned integer type
like `u32`, which ensures the parameter is never negative.

On the other hand, when errors happen due to a problem _outside_ the program,
that is almost always something you should handle with a `Result`, even if
there's nothing more that the program can do but print an error message and
exit. We've already seen that converting strings into numbers with `parse`
returns a `Result`, because strings containing invalid numbers are usually
_not_ bugs; they are usually because the _input_ to the program was
incorrect. When you try to access files and get errors from the operating
system, that is also not a bug in your program; at worst it indicates that
there's something wrong with the _computer_ that a human needs to fix (Unix
systems don't work very well if `/etc/passwd` doesn't exist). Network servers
might be inaccessible or malfunctioning for reasons completely out of your
control, or they might be refusing service because the user doesn't have the
proper credentials.

Another thing to keep in mind is that when you use `panic!` you are declaring
that there is nothing a caller could possibly do to recover from the error.
When you choose to return a `Result` value, you give your caller options,
rather than making the decision for them. They could choose to attempt to
recover in a way that’s appropriate for their situation, or they could decide
that actually, an `Err` value in this case is unrecoverable, so they can call
`panic!` and turn your recoverable error into an unrecoverable one.
Therefore, returning `Result` is a good default choice when you’re defining a
function that might fail.

There are a few situations in which it’s more appropriate to write code that
panics instead of returning a `Result`, but they are less common. Let’s discuss
why it’s appropriate to panic in examples, prototype code, and tests, then
situations where you as a human can know a method won’t fail that the compiler
can’t reason about, and conclude with some general guidelines on how to decide
whether to panic in library code.
### Reporting Errors to the User

At the highest levels of your program, you will need to intercept error
`Results` and report them to a human. Exactly how to do this will depend on
the environment in which you are running—a command-line tool is different from
a network server or a graphical application. Rust's standard library has all
the facilities needed to report errors in command-line tools, so we will use
that situation for an example.

Recall from the previous chapter, the function that read text from a file.

``` rust
use std::io;
use std::io::Read;
use std::fs::File;

fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();

File::open("hello.txt")?.read_to_string(&mut s)?;

Ok(s)
}
```

If we call this function from `main` in a command-line tool, what should we do
when it returns an error? It's not right to use `expect` or `panic!`, but we
can't use the `?` operator either, because `main` doesn't return anything.
Here's one way to handle it:

``` rust,ignore
use std::process;

fn main() {
match read_username_from_file() {
Ok(s) => {
println!("Hello, {}.", s);
},
Err(e) => {
eprintln!("Failed to read from 'hello.txt': {}", e);
process::exit(1);
}
}
}
```

This uses two library features we haven't seen before. `eprintln!` is like
`println!`, except for one thing: it prints text to the standard _error_
stream, instead of standard output. This prevents error messages from getting
mixed up with the "normal" output of the program—if you've ever tried to print
a document on fancy paper, but what got printed was error messages, you'll
understand why this is important. (The messages printed by `panic!` are
also sent to standard error.)

`process::exit(1)` ends the program with an _unsuccessful_ "exit code"
reported to the command-line environment. (By convention, an exit code of zero
means success, and any nonzero value is some sort of failure.) Rust doesn't
let you do this by returning a value from `main`, the way C does, because the
whole idea of an "exit code" is peculiar to multiprocessing operating systems
that work essentially the same way Unix does. In other environments it isn't
possible to return an "exit code", or it might even be a bug to return from
`main` at all. But if you're in an environment where exit codes exist, then
`process::exit` will be available, and the number passed to it will mean the
same thing it does in C. And falling off the end of `main`, which is what
happens in the `Ok` case, will be the same as calling `process::exit(0)`.

### Examples, Prototype Code, and Tests: Perfectly Fine to Panic

Expand Down Expand Up @@ -65,61 +161,6 @@ being hardcoded into the program, and therefore *did* have a possibility of
failure, we’d definitely want to handle the `Result` in a more robust way
instead.

### Guidelines for Error Handling

It’s advisable to have your code `panic!` when it’s possible that you could end
up in a bad state—in this context, bad state is when some assumption,
guarantee, contract, or invariant has been broken, such as when invalid values,
contradictory values, or missing values are passed to your code—plus one or
more of the following:

* The bad state is not something that’s *expected* to happen occasionally
* Your code after this point needs to rely on not being in this bad state
* There’s not a good way to encode this information in the types you use

If someone calls your code and passes in values that don’t make sense, the best
thing might be to `panic!` and alert the person using your library to the bug
in their code so that they can fix it during development. Similarly, `panic!`
is often appropriate if you’re calling external code that is out of your
control, and it returns an invalid state that you have no way of fixing.

When a bad state is reached, but it’s expected to happen no matter how well you
write your code, it’s still more appropriate to return a `Result` rather than
calling `panic!`. Examples of this include a parser being given malformed data,
or an HTTP request returning a status that indicates you have hit a rate limit.
In these cases, you should indicate that failure is an expected possibility by
returning a `Result` in order to propagate these bad states upwards so that the
caller can decide how they would like to handle the problem. To `panic!`
wouldn’t be the best way to handle these cases.

When your code performs operations on values, your code should verify the
values are valid first, and `panic!` if the values aren’t valid. This is mostly
for safety reasons: attempting to operate on invalid data can expose your code
to vulnerabilities. This is the main reason that the standard library will
`panic!` if you attempt an out-of-bounds array access: trying to access memory
that doesn’t belong to the current data structure is a common security problem.
Functions often have *contracts*: their behavior is only guaranteed if the
inputs meet particular requirements. Panicking when the contract is violated
makes sense because a contract violation always indicates a caller-side bug,
and it is not a kind of error you want callers to have to explicitly handle. In
fact, there’s no reasonable way for calling code to recover: the calling
*programmers* need to fix the code. Contracts for a function, especially when a
violation will cause a panic, should be explained in the API documentation for
the function.

Having lots of error checks in all of your functions would be verbose and
annoying, though. Luckily, you can use Rust’s type system (and thus the type
checking the compiler does) to do a lot of the checks for you. If your function
has a particular type as a parameter, you can proceed with your code’s logic
knowing that the compiler has already ensured you have a valid value. For
example, if you have a type rather than an `Option`, your program expects to
have *something* rather than *nothing*. Your code then doesn’t have to handle
two cases for the `Some` and `None` variants, it will only have one case for
definitely having a value. Code trying to pass nothing to your function won’t
even compile, so your function doesn’t have to check for that case at runtime.
Another example is using an unsigned integer type like `u32`, which ensures the
parameter is never negative.

### Creating Custom Types for Validation

Let’s take the idea of using Rust’s type system to ensure we have a valid value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,7 @@ fn main() {
let args: Vec<String> = env::args().collect();

let config = Config::new(&args).unwrap_or_else(|err| {
println!("Problem parsing arguments: {}", err);
eprintln!("Problem parsing arguments: {}", err);
process::exit(1);
});

Expand Down Expand Up @@ -349,12 +349,12 @@ pass the inner value of the `Err` to our closure in the parameter `err` that
appears between the vertical pipes. Using `unwrap_or_else` lets us do some
custom, non-`panic!` error handling.

Said error handling is only two lines: we print out the error, then call
`std::process::exit`. That function will stop our program's execution
immediately and return the number passed to it as a return code. By convention,
a zero means success and any other value means failure. In the end, this has
similar characteristics to our `panic!`-based handling we had in Listing 12-7,
but we no longer get all the extra output. Let's try it:
That custom error handling is very similar to what we saw in Chapter 9.3. We
print out the error with `eprintln!`, so that it is not confused with the
non-error output of the program, and then we use `std::process::exit` to end
the program with a unsuccessful exit code. This is not that different from
the `panic!` we used in Listing 12-7, but we no longer get all the extra
output. Let's try it:

```text
$ cargo run
Expand Down Expand Up @@ -481,8 +481,7 @@ fn main() {
println!("In file {}", config.filename);

if let Err(e) = run(config) {
println!("Application error: {}", e);

eprintln!("Application error: {}", e);
process::exit(1);
}
}
Expand Down Expand Up @@ -593,16 +592,15 @@ fn main() {
let args: Vec<String> = env::args().collect();

let config = Config::new(&args).unwrap_or_else(|err| {
println!("Problem parsing arguments: {}", err);
eprintln!("Problem parsing arguments: {}", err);
process::exit(1);
});

println!("Searching for {}", config.search);
println!("In file {}", config.filename);

if let Err(e) = greprs::run(config) {
println!("Application error: {}", e);

eprintln!("Application error: {}", e);
process::exit(1);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,9 @@ improve it.

Now that the `grep` function is working, we need to do one last thing inside of
the `run` function: we never printed out the results! We'll do that by adding
a `for` loop that prints each line returned from the `grep` function:
a `for` loop that prints each line returned from the `grep` function.
This output is the "normal" output of the program—not an error
message—so we use `println!` for it:

<span class="filename">Filename: src/lib.rs</span>

Expand Down
Loading