@@ -15,221 +15,22 @@ fn check_guess(number: u32) -> bool {
15
15
16
16
This function accepts a guess between zero and a hundred, and checks if it's
17
17
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:
112
20
113
21
``` 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];
131
23
132
- number == 34
133
- }
134
-
135
- fn main() {
136
- step1();
137
- }
24
+ v[1000]; // this will panic
138
25
```
139
26
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.
155
30
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."
228
36
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