Skip to content

Commit caceeac

Browse files
committed
move towards vectors
1 parent be5258d commit caceeac

File tree

1 file changed

+12
-211
lines changed

1 file changed

+12
-211
lines changed

src/ch07-02-unrecoverable-errors-with-panic.md

Lines changed: 12 additions & 211 deletions
Original file line numberDiff line numberDiff line change
@@ -15,221 +15,22 @@ fn check_guess(number: u32) -> bool {
1515

1616
This function accepts a guess between zero and a hundred, and checks if it's
1717
equivalent to the correct number, which is `34` in this case. It's kind of a
18-
silly function, but we need an example, so it works.
19-
20-
There's no number type for "between zero and a hundred" in Rust, so we are
21-
accepting a `u32`, and then checking in the function's body to make sure the
22-
guess is in-bounds. If the number is too big, there's been an error: someone
23-
made a mistake. We then invoke `panic!` to say "something went wrong, we cannot
24-
continue to run this program."
25-
26-
Checking some sort of condition and then `panic!`ing if something is wrong is a
27-
common use of `panic!`. In fact, there's a second macro in Rust, `assert!` for
28-
this case. `assert!` will check some kind of condition, and `panic!` if the
29-
condition is false. We could also write our function like this:
30-
31-
```rust
32-
fn check_guess(number: u32) -> bool {
33-
assert!(number < 100);
34-
35-
number == 34
36-
}
37-
```
38-
39-
If we try and use `check_guess` in a program, and make an error:
40-
41-
```rust,should_panic
42-
fn check_guess(number: u32) -> bool {
43-
assert!(number < 100);
44-
45-
number == 34
46-
}
47-
48-
fn main() {
49-
let answer = check_guess(5);
50-
println!("answer was: {}", answer);
51-
52-
let answer = check_guess(34);
53-
println!("answer was: {}", answer);
54-
55-
let answer = check_guess(500);
56-
println!("answer was: {}", answer);
57-
}
58-
```
59-
60-
We'll see output like this:
61-
62-
```text
63-
answer was: false
64-
answer was: true
65-
66-
thread '<main>' panicked at 'assertion failed: number < 100', <anon>:2
67-
```
68-
69-
First, `5` was okay, but false. Then, `34` was okay, but true. Finally, `500`
70-
caused a panic.
71-
72-
Panics cause your program to stop executing. To check this, we could move the
73-
failing case above the good cases:
74-
75-
```rust,should_panic
76-
# fn check_guess(number: u32) -> bool {
77-
# assert!(number < 100);
78-
#
79-
# number == 34
80-
# }
81-
#
82-
fn main() {
83-
let answer = check_guess(500);
84-
println!("answer was: {}", answer);
85-
86-
let answer = check_guess(5);
87-
println!("answer was: {}", answer);
88-
89-
let answer = check_guess(34);
90-
println!("answer was: {}", answer);
91-
}
92-
```
93-
94-
If we run it, we'll see that we never check `5` or `34`:
95-
96-
```text
97-
thread '<main>' panicked at 'assertion failed: number < 100', <anon>:2
98-
```
99-
100-
This is why we call `panic!` an unrecoverable error: the other code never gets a
101-
chance to run. Our program just ends.
102-
103-
But what does it mean to "end a program"? As it turns out, there are multiple
104-
strategies for processing an unrecoverable error. The two main ones are
105-
'unwinding' and 'aborting'.
106-
107-
## Unwinding
108-
109-
By default, when a `panic!` happens in Rust, it starts doing something called
110-
"unwinding". To explain unwinding, let's consider a slightly more complex
111-
program:
18+
silly function, but it's similar to a real example you've already seen:
19+
indexing vectors:
11220

11321
```rust,should_panic
114-
fn step1() {
115-
let s = String::from("Step 1");
116-
step2();
117-
}
118-
119-
fn step2() {
120-
let s = String::from("Step 2");
121-
step3();
122-
}
123-
124-
fn step3() {
125-
let s = String::from("Step 3");
126-
check_guess(500);
127-
}
128-
129-
fn check_guess(number: u32) -> bool {
130-
assert!(number < 100);
22+
let v = vec![1, 2, 3];
13123
132-
number == 34
133-
}
134-
135-
fn main() {
136-
step1();
137-
}
24+
v[1000]; // this will panic
13825
```
13926

140-
Here, we have four functions total: `step1` calls `step2` which calls `step3`
141-
which then calls `check_guess`. Something like this diagram, with each of the
142-
variable bindings written in:
143-
144-
```text
145-
> main
146-
|
147-
> step1 String: "Step 1"
148-
|
149-
> step2 String: "Step 2"
150-
|
151-
> step3 String: "Step 3"
152-
|
153-
> check_guess: u32: 500
154-
```
27+
The implementation of indexing with `[]` looks similar to our `check_guess`
28+
function above: check if the number is larger than the length of the vector,
29+
and if it is, panic.
15530

156-
When `check_guess` causes a `panic!` via `assert!`, it will walk back through
157-
each of these functions, and clean them up. We haven't yet talked about
158-
destructors in Rust, that'll come in Chapter XX. For now, think about it this
159-
way: simple values like that `u32` can be destroyed by freeing their memory.
160-
More complicated values, like `String`s, have more complicated needs. In these
161-
cases, there is a function that does this cleanup. We call this a "drop
162-
function" in Rust.
163-
164-
So, the first thing that will happen is that `check_guess`, our current
165-
function, gets cleaned up. There's only one value, the `500`, and so its memory
166-
will be freed. Now our program looks like this:
167-
168-
```text
169-
> main
170-
|
171-
> step1 String: "Step 1"
172-
|
173-
> step2 String: "Step 2"
174-
|
175-
> step3 String: "Step 3"
176-
```
177-
178-
Now we're on `step3`. In the same way, Rust will call the `String`'s drop
179-
function, deallocating the `String`. Once that's done, we can move on:
180-
181-
```text
182-
> main
183-
|
184-
> step1 String: "Step 1"
185-
|
186-
> step2 String: "Step 2"
187-
```
188-
189-
The pattern continues: `step2` has a single value, and `"Step 2"` has its
190-
drop function called. Next!
191-
192-
```text
193-
> main
194-
|
195-
> step1 String: "Step 1"
196-
```
197-
198-
Almost done! `step1` also has a `String`, so Rust will invoke its drop function.
199-
200-
```text
201-
> main
202-
```
203-
204-
Finally, all we have left is `main()`. It has no variable bindings or arguments
205-
in this case, so there's nothing to clean up. Our whole program has been delt
206-
with, and so terminates execution, after printing a message.
207-
208-
## Aborting
209-
210-
Doing all that is a lot of work! And the end result is that our program
211-
terminates. Handing panics with unwinding is useful in many scenarios, but some
212-
applications would rather skip straight to the end, and 'abort'. With some
213-
configuration, Cargo will allow us to use this alternate implementation of
214-
`panic!`. What happens when that's enabled? Let's consider what our call stack
215-
looked like:
216-
217-
```text
218-
> main
219-
|
220-
> step1 String: "Step 1"
221-
|
222-
> step2 String: "Step 2"
223-
|
224-
> step3 String: "Step 3"
225-
|
226-
> check_guess: u32: 500
227-
```
31+
Why do we need to panic? There's no number type for "between zero and a
32+
hundred" in Rust, so we are accepting a `u32`, and then checking in the
33+
function's body to make sure the guess is in-bounds. If the number is too big,
34+
there's been an error: someone made a mistake. We then invoke `panic!` to say
35+
"something went wrong, we cannot continue to run this program."
22836

229-
With an abort implementation of `panic!`, instead of walking back up through the
230-
previous functions and cleaning everything up, we skip straight to the end: our
231-
program terminates. But what about our resources? In the case of memory, like is
232-
the case with `String`s, the operating system will reclaim the memory. So for
233-
this program, the two cases are identical, but aborting is much more efficient.
234-
Other, more complex resources work differently, however, and so aborting may not
235-
always be the right choice either. It depends!

0 commit comments

Comments
 (0)