1
00:00:00,240 --> 00:00:02,099
Welcome to module 2 about
2
00:00:02,299 --> 00:00:02,790
panicking.
3
00:00:04,360 --> 00:00:06,489
In this module, we'll define what panic
4
00:00:06,689 --> 00:00:07,360
means in Rust
5
00:00:07,780 --> 00:00:09,850
and then we'll look at how to use the panic! macro
6
00:00:10,050 --> 00:00:11,410
to cause your program to panic.
7
00:00:11,870 --> 00:00:13,689
We'll discuss situations in which you
8
00:00:13,889 --> 00:00:15,190
should cause your program to panic
9
00:00:15,390 --> 00:00:16,540
and look at some examples.
10
00:00:17,080 --> 00:00:19,059
And we'll round out this module by looking
11
00:00:19,259 --> 00:00:21,009
at some other macros that ultimately call
12
00:00:21,209 --> 00:00:21,940
the panic! macro
13
00:00:22,330 --> 00:00:24,159
but provide different semantics for some other
14
00:00:24,359 --> 00:00:24,990
situations.
15
00:00:26,540 --> 00:00:28,340
So, what is panicking?
16
00:00:29,870 --> 00:00:31,730
Imagine a thread of your program
17
00:00:31,970 --> 00:00:33,560
as the machine in a factory
18
00:00:33,980 --> 00:00:36,229
chugging along, happily processing
19
00:00:36,429 --> 00:00:37,370
the data that it's given.
20
00:00:38,850 --> 00:00:40,830
Then, it comes across a value
21
00:00:41,030 --> 00:00:42,450
that doesn't make any sense.
22
00:00:42,990 --> 00:00:45,090
Processing that value in the same way
23
00:00:45,290 --> 00:00:46,650
as the values that make sense
24
00:00:47,010 --> 00:00:49,350
would result in garbage at best
25
00:00:49,550 --> 00:00:51,920
and would open up the system to vulnerabilities
26
00:00:52,120 --> 00:00:52,970
at worse.
27
00:00:54,750 --> 00:00:56,879
Rather than continuing normal processing
28
00:00:57,079 --> 00:00:58,140
with an invalid input,
29
00:00:58,500 --> 00:01:00,359
panicking is like an emergency stop
30
00:01:00,559 --> 00:01:00,810
button.
31
00:01:01,890 --> 00:01:03,540
Pushing the emergency stop button
32
00:01:03,930 --> 00:01:05,789
causes the thread to stop what it's doing
33
00:01:05,989 --> 00:01:06,680
immediately instead.
34
00:01:08,280 --> 00:01:10,380
This prevents the program from continuing
35
00:01:10,580 --> 00:01:12,330
with garbage that might be incorrect
36
00:01:12,530 --> 00:01:13,250
or dangerous.
37
00:01:14,820 --> 00:01:16,620
Here's an example in a program.
38
00:01:17,230 --> 00:01:19,020
Say we're writing a command-line program
39
00:01:19,220 --> 00:01:21,030
that can process data for Windows,
40
00:01:21,230 --> 00:01:22,680
Linux, or macOS.
41
00:01:23,190 --> 00:01:25,229
The user of the program will specify
42
00:01:25,429 --> 00:01:27,270
the operating system they want to process data
43
00:01:27,470 --> 00:01:28,990
for by using the -p
44
00:01:29,220 --> 00:01:30,710
flag for platform.
45
00:01:32,270 --> 00:01:33,600
What should the program do
46
00:01:33,800 --> 00:01:36,140
if the user enters a value like oranges
47
00:01:36,340 --> 00:01:38,240
that isn't one of the operating systems it
48
00:01:38,440 --> 00:01:39,720
knows how to process data for?
49
00:01:41,250 --> 00:01:43,500
Picking an arbitrary operating system
50
00:01:43,700 --> 00:01:45,480
and continuing would be incorrect.
51
00:01:46,080 --> 00:01:48,200
The user clearly meant to specify something
52
00:01:48,510 --> 00:01:50,720
but we're not likely to guess what they wanted correctly.
53
00:01:52,260 --> 00:01:54,089
The right thing to do in this case is to
54
00:01:54,289 --> 00:01:55,020
stop the thread
55
00:01:55,220 --> 00:01:56,990
with the message to the user that the
56
00:01:57,190 --> 00:01:58,319
program can't do anything
57
00:01:58,519 --> 00:01:59,550
with this invalid value,
58
00:01:59,880 --> 00:02:01,440
and they need to start the program again
59
00:02:01,640 --> 00:02:02,480
with a different value.
60
00:02:04,030 --> 00:02:05,470
Let's see how to write this in code
61
00:02:05,670 --> 00:02:06,610
with the panic! macro.
62
00:02:08,130 --> 00:02:10,138
You can call the panic! macro at any point
63
00:02:10,338 --> 00:02:10,700
in your code.
64
00:02:11,340 --> 00:02:13,180
In this first example, we're calling
65
00:02:13,380 --> 00:02:15,089
panic! as the only action taken in
66
00:02:15,289 --> 00:02:15,450
main.
67
00:02:16,940 --> 00:02:18,920
If we don't specify any arguments,
68
00:02:19,250 --> 00:02:21,710
when we run this code, we'll see the message,
69
00:02:21,910 --> 00:02:24,050
thread 'main' panicked at 'explicit panic',
70
00:02:24,590 --> 00:02:26,490
and the location in our code where the panic
71
00:02:26,690 --> 00:02:27,230
came from.
72
00:02:28,730 --> 00:02:30,960
If we specify a message as an argument
73
00:02:31,160 --> 00:02:31,490
to panic!,
74
00:02:32,520 --> 00:02:34,290
then that message appears in the output
75
00:02:34,570 --> 00:02:36,010
instead of 'explicit panic'.
76
00:02:37,470 --> 00:02:39,389
You can also give panic! the same kinds
77
00:02:39,589 --> 00:02:41,370
of arguments you can give to println,
78
00:02:41,790 --> 00:02:42,659
a format string
79
00:02:42,859 --> 00:02:43,620
with placeholders
80
00:02:43,920 --> 00:02:44,819
and variables that go
81
00:02:45,019 --> 00:02:45,900
with those placeholders.
82
00:02:46,560 --> 00:02:48,660
This lets you explain better what you got
83
00:02:48,860 --> 00:02:49,800
that wasn't expected.
84
00:02:51,330 --> 00:02:52,870
This panic call prints
85
00:02:53,140 --> 00:02:54,350
'Something's not right here!
86
00:02:54,700 --> 00:02:57,100
Got value: no' as its message.
87
00:02:58,460 --> 00:02:59,580
Let's put into code
88
00:02:59,780 --> 00:03:01,680
the logic we talked about for the command-line
89
00:03:01,880 --> 00:03:03,819
application that can produce
90
00:03:04,019 --> 00:03:05,679
different output for different operating
91
00:03:05,879 --> 00:03:06,340
systems.
92
00:03:06,690 --> 00:03:08,800
Here, we've defined an enum
93
00:03:09,000 --> 00:03:09,760
for the platforms
94
00:03:09,960 --> 00:03:11,020
our program supports.
95
00:03:11,500 --> 00:03:13,330
We have a parse function, defined
96
00:03:13,530 --> 00:03:14,770
as a function associated
97
00:03:14,970 --> 00:03:16,100
with the Platform enum
98
00:03:16,710 --> 00:03:18,370
that takes an argument named platform_arg.
99
00:03:19,410 --> 00:03:21,250
platform_arg represents
100
00:03:21,450 --> 00:03:23,530
the value you would get from the command-line argument.
101
00:03:24,310 --> 00:03:26,230
We've excluded the actual command-line argument
102
00:03:26,430 --> 00:03:28,190
parsing to simplify this example.
103
00:03:28,720 --> 00:03:30,609
The parse function checks to see if
104
00:03:30,809 --> 00:03:31,990
the platform_arg value
105
00:03:32,290 --> 00:03:34,140
is any of the values we expect
106
00:03:34,840 --> 00:03:36,669
and, if so, returns
107
00:03:36,869 --> 00:03:38,260
the corresponding enum variant.
108
00:03:38,830 --> 00:03:40,719
In the else case, we call the
109
00:03:40,919 --> 00:03:41,680
panic! macro
110
00:03:41,880 --> 00:03:43,680
with the value that we didn't recognize
111
00:03:43,880 --> 00:03:44,830
and stop the program.
112
00:03:46,360 --> 00:03:48,610
In main, we call the parse function
113
00:03:48,820 --> 00:03:50,919
and then we have a println that uses
114
00:03:51,119 --> 00:03:52,320
the returned platform value.
115
00:03:53,800 --> 00:03:55,090
If we run this example
116
00:03:55,290 --> 00:03:57,369
with the string Windows for platform_arg,
117
00:03:57,569 --> 00:03:59,209
that value is
118
00:03:59,409 --> 00:04:01,070
recognised and we get to the final print
119
00:04:01,270 --> 00:04:01,420
line.
120
00:04:02,910 --> 00:04:04,710
If we change the platform_arg value
121
00:04:04,910 --> 00:04:06,540
to something that we don't recognize
122
00:04:06,740 --> 00:04:07,680
as an operating system,
123
00:04:08,670 --> 00:04:10,559
when we run it, the program stops with the
124
00:04:10,759 --> 00:04:11,430
panic message
125
00:04:11,630 --> 00:04:13,120
and never gets to the final print line.
126
00:04:14,640 --> 00:04:17,010
Let's talk some more about when it's good to panic
127
00:04:17,279 --> 00:04:18,810
and look at a few more examples.
128
00:04:20,279 --> 00:04:22,018
If you're trying to decide whether
129
00:04:22,218 --> 00:04:24,079
or not to panic, consider whether
130
00:04:24,279 --> 00:04:26,039
taking the next action would ever be
131
00:04:26,239 --> 00:04:27,870
valid given the situation you're in.
132
00:04:28,350 --> 00:04:30,330
For example, there is no default
133
00:04:30,530 --> 00:04:32,100
all users would find reasonable.
134
00:04:32,940 --> 00:04:34,830
The command-line argument example fits
135
00:04:35,030 --> 00:04:35,850
into this case.
136
00:04:37,350 --> 00:04:39,329
Another example is out-of-bounds
137
00:04:39,529 --> 00:04:41,550
memory access, which we discussed
138
00:04:41,750 --> 00:04:43,700
in the Slices module in unit 2.
139
00:04:44,190 --> 00:04:46,259
If we have, say, a vector of three
140
00:04:46,459 --> 00:04:46,890
items
141
00:04:47,340 --> 00:04:48,690
and we try to access an item
142
00:04:48,890 --> 00:04:49,800
in index 50,
143
00:04:51,330 --> 00:04:53,040
when we run this, Rust will panic
144
00:04:53,240 --> 00:04:54,990
and say the index is out of bounds.
145
00:04:56,490 --> 00:04:58,540
In this case, panicking is important
146
00:04:58,740 --> 00:05:00,510
to prevent a security vulnerability
147
00:05:00,710 --> 00:05:02,150
called a buffer overread.
148
00:05:02,970 --> 00:05:04,889
C will happily return to you memory
149
00:05:05,089 --> 00:05:07,080
that doesn't belong to the current data structure
150
00:05:07,590 --> 00:05:09,470
and this has been an entry point of lots
151
00:05:09,670 --> 00:05:10,320
of exploits.
152
00:05:11,810 --> 00:05:13,759
Another aspect to consider is that,
153
00:05:13,959 --> 00:05:16,069
by panicking, you're making the decision
154
00:05:16,269 --> 00:05:17,870
for any code calling this function that
155
00:05:18,330 --> 00:05:20,209
there's no way for it to try to recover
156
00:05:20,409 --> 00:05:22,069
either. We're going to talk
157
00:05:22,269 --> 00:05:24,470
about recovering from errors in the coming modules.
158
00:05:25,960 --> 00:05:28,029
Another time to panic is when the problem
159
00:05:28,229 --> 00:05:29,650
must be fixed by a programmer
160
00:05:29,850 --> 00:05:31,690
changing buggy code, not
161
00:05:31,890 --> 00:05:33,100
a user changing their input.
162
00:05:34,110 --> 00:05:35,610
Let's look at an example from the HTTP
163
00:05:36,140 --> 00:05:36,400
crate.
164
00:05:37,910 --> 00:05:39,859
This is the documentation for the header
165
00:05:40,059 --> 00:05:41,630
name from_static function,
166
00:05:42,020 --> 00:05:44,390
one of the ways to create a HeaderName instance.
167
00:05:45,900 --> 00:05:47,700
This function takes a string literal,
168
00:05:47,900 --> 00:05:49,230
which you'll recall from the Slices
169
00:05:49,430 --> 00:05:51,300
module in unit 2 is a string
170
00:05:51,500 --> 00:05:53,090
that literally appears in your source code.
171
00:05:53,760 --> 00:05:55,589
If the string literal provided
172
00:05:55,789 --> 00:05:57,509
isn't a valid HeaderName, such
173
00:05:57,709 --> 00:05:59,430
as a string containing special characters,
174
00:06:00,460 --> 00:06:02,260
this function will panic to alert you
175
00:06:02,460 --> 00:06:04,430
that your hardcoded string is incorrect.
176
00:06:05,990 --> 00:06:07,939
The only way to fix this panic is
177
00:06:08,139 --> 00:06:08,980
to edit the string literal
178
00:06:09,180 --> 00:06:10,570
in the code and recompile.
179
00:06:10,960 --> 00:06:12,949
There's no way an end user could fix
180
00:06:13,149 --> 00:06:13,370
this.
181
00:06:14,890 --> 00:06:16,450
A time when you shouldn't panic
182
00:06:16,870 --> 00:06:18,820
is when failure is the normal occurrence
183
00:06:19,020 --> 00:06:20,320
given the operation you're doing.
184
00:06:21,810 --> 00:06:23,730
For example, web browsers
185
00:06:23,930 --> 00:06:26,040
might receive malformed HTML.
186
00:06:26,880 --> 00:06:28,049
If your browser panicked
187
00:06:28,249 --> 00:06:30,059
and shut down every time you visited
188
00:06:30,259 --> 00:06:32,270
a site that didn't have valid HTML,
189
00:06:32,660 --> 00:06:33,840
you'd probably get frustrated
190
00:06:34,040 --> 00:06:35,070
and use a different browser.
191
00:06:36,570 --> 00:06:38,729
Instead, your browser takes various
192
00:06:38,929 --> 00:06:39,870
efforts to recover
193
00:06:40,200 --> 00:06:42,059
and present what it can in the face
194
00:06:42,259 --> 00:06:43,350
of invalid HTML.
195
00:06:44,840 --> 00:06:46,160
Despite these guidelines,
196
00:06:46,400 --> 00:06:48,379
you can always choose to panic before you've
197
00:06:48,579 --> 00:06:50,000
decided how to handle errors,
198
00:06:50,540 --> 00:06:52,370
then you can search for calls to panic
199
00:06:52,570 --> 00:06:54,440
to update your error handling code later.
200
00:06:55,940 --> 00:06:56,570
Finally,
201
00:06:56,780 --> 00:06:58,610
let's talk about a few other macros
202
00:06:58,810 --> 00:07:00,290
provided by the Standard Library
203
00:07:00,530 --> 00:07:01,720
that also cause a panic!.
204
00:07:03,200 --> 00:07:05,110
First is the unreachable! macro.
205
00:07:06,020 --> 00:07:08,180
This is useful for indicating to the compiler
206
00:07:08,450 --> 00:07:10,490
that it's impossible to get to a certain point
207
00:07:10,690 --> 00:07:11,060
in the code.
208
00:07:12,550 --> 00:07:14,649
This is useful when we're required to cover
209
00:07:14,849 --> 00:07:16,479
all cases that the compiler thinks are
210
00:07:16,679 --> 00:07:17,080
possible,
211
00:07:17,650 --> 00:07:19,120
but because of some other constraint,
212
00:07:19,570 --> 00:07:21,489
we as humans know certain cases aren't
213
00:07:21,689 --> 00:07:22,040
possible.
214
00:07:23,530 --> 00:07:25,480
For example, here's a program
215
00:07:25,680 --> 00:07:27,370
with an enum for the state of a door,
216
00:07:27,820 --> 00:07:28,810
which can be Opened
217
00:07:29,010 --> 00:07:29,500
or Closed.
218
00:07:30,550 --> 00:07:32,439
We also have an enum for the actions that
219
00:07:32,639 --> 00:07:34,419
can be taken on the door, which
220
00:07:34,619 --> 00:07:35,830
are Open and Close.
221
00:07:36,810 --> 00:07:38,670
In a function that takes the current state
222
00:07:38,870 --> 00:07:40,559
of a door and an action to take
223
00:07:40,759 --> 00:07:41,100
on the door,
224
00:07:41,880 --> 00:07:43,709
we need to handle how to close an open
225
00:07:43,909 --> 00:07:44,070
door
226
00:07:44,490 --> 00:07:46,080
and how to open a closed door.
227
00:07:47,010 --> 00:07:48,959
The other two states, opening an open
228
00:07:49,159 --> 00:07:50,820
door and closing a closed door,
229
00:07:51,240 --> 00:07:52,240
shouldn't ever happen.
230
00:07:53,230 --> 00:07:55,210
Rust requires a match expression
231
00:07:55,410 --> 00:07:57,129
to cover all cases, so
232
00:07:57,329 --> 00:07:59,210
we can use the underscore to catch the impossible
233
00:07:59,410 --> 00:08:01,510
cases and call it the unreachable! macro.
234
00:08:03,980 --> 00:08:04,820
If we're wrong
235
00:08:05,020 --> 00:08:06,739
and somehow the program does get to
236
00:08:06,939 --> 00:08:08,599
this spot in the code, such
237
00:08:08,799 --> 00:08:10,519
as with this main function where we're calling
238
00:08:10,719 --> 00:08:12,410
the function with the unsupported values,
239
00:08:13,340 --> 00:08:15,230
the unreachable! macro will call the panic!
240
00:08:15,430 --> 00:08:15,770
macro
241
00:08:16,100 --> 00:08:17,900
with the message, 'internal error:
242
00:08:18,100 --> 00:08:19,280
entered unreachable code'.
243
00:08:20,810 --> 00:08:22,890
It's preferable to use unreachable!
244
00:08:23,090 --> 00:08:24,810
rather than a panic! in this situation
245
00:08:25,200 --> 00:08:27,269
because it conveys our intent to future
246
00:08:27,469 --> 00:08:28,290
readers of this code.
247
00:08:29,780 --> 00:08:31,880
The next macro, unimplemented!,
248
00:08:32,299 --> 00:08:34,570
is useful when starting work on a new feature.
249
00:08:35,330 --> 00:08:37,259
This macro communicates that we intend
250
00:08:37,459 --> 00:08:38,870
to write code that goes in its place
251
00:08:39,070 --> 00:08:39,710
but we haven't yet.
252
00:08:41,230 --> 00:08:43,450
In our previous example, where we had comments
253
00:08:43,650 --> 00:08:45,340
showing where we would add code to handle
254
00:08:45,540 --> 00:08:47,259
door actions, we could instead
255
00:08:47,459 --> 00:08:49,450
have put calls to the unimplemented! macro.
256
00:08:51,060 --> 00:08:53,310
If a program gets to an unimplemented case,
257
00:08:53,760 --> 00:08:55,690
it will panic with the message, 'not yet
258
00:08:55,890 --> 00:08:56,180
implemented'.
259
00:08:58,030 --> 00:09:00,100
The assert family of macros will panic
260
00:09:00,300 --> 00:09:01,990
if a given condition isn't true.
261
00:09:02,620 --> 00:09:03,700
assert! takes a Boolean
262
00:09:03,900 --> 00:09:05,710
value and panics if it is false.
263
00:09:06,340 --> 00:09:08,240
assert_eq! takes two values
264
00:09:08,440 --> 00:09:10,150
and panics if they aren't equivalent,
265
00:09:10,690 --> 00:09:12,729
and assert_ne! takes two
266
00:09:12,929 --> 00:09:14,859
values and panics if they are equivalent.
267
00:09:16,570 --> 00:09:18,489
These macros are commonly used in
268
00:09:18,689 --> 00:09:19,600
two situations.
269
00:09:20,840 --> 00:09:23,210
The first situation is to check preconditions
270
00:09:23,410 --> 00:09:24,590
at the beginning of a function.
271
00:09:25,100 --> 00:09:26,360
In the rest of the function,
272
00:09:26,570 --> 00:09:28,609
you can proceed knowing these conditions
273
00:09:28,809 --> 00:09:30,679
are true and not have to check them in multiple
274
00:09:30,879 --> 00:09:32,539
locations because the program
275
00:09:32,739 --> 00:09:34,640
would have stopped earlier if they weren't true.
276
00:09:36,190 --> 00:09:38,260
The second, much more common, situation
277
00:09:38,460 --> 00:09:40,209
the assert macros are used in, is in
278
00:09:40,409 --> 00:09:40,780
tests.
279
00:09:41,800 --> 00:09:43,750
The way test functions work in Rust
280
00:09:43,950 --> 00:09:45,729
is that if the code inside a test function
281
00:09:45,929 --> 00:09:47,590
panics, that test fails.
282
00:09:48,330 --> 00:09:50,259
The assert macros make it convenient to write
283
00:09:50,459 --> 00:09:52,239
clear tests that compare the values
284
00:09:52,439 --> 00:09:53,570
you expect with the values
285
00:09:53,770 --> 00:09:55,210
the code under test produces.
286
00:09:56,730 --> 00:09:57,630
In this module,
287
00:09:57,830 --> 00:10:00,090
we discussed that panicking is stopping the program
288
00:10:00,290 --> 00:10:01,710
if it's in an invalid state
289
00:10:02,220 --> 00:10:04,410
and that we do this by calling the panic! macro
290
00:10:04,610 --> 00:10:05,790
with an optional message.
291
00:10:06,810 --> 00:10:08,740
We discussed how it's a good idea to panic
292
00:10:08,940 --> 00:10:10,470
when continuing would be incorrect,
293
00:10:10,950 --> 00:10:12,840
there is no way for the calling code to recover,
294
00:10:13,350 --> 00:10:15,209
the problem must be fixed by changing the
295
00:10:15,409 --> 00:10:16,770
code rather than inputs,
296
00:10:17,280 --> 00:10:19,590
when failure isn't expected to happen regularly,
297
00:10:20,070 --> 00:10:21,810
and when we haven't decided how we want
298
00:10:22,080 --> 00:10:23,070
to handle errors.
299
00:10:24,070 --> 00:10:26,020
We talked about other macros that panic,
300
00:10:26,590 --> 00:10:27,830
including unreachable!,
301
00:10:28,270 --> 00:10:29,290
unimplemented!, and
302
00:10:29,840 --> 00:10:31,110
the assert macros.
303
00:10:32,620 --> 00:10:33,610
In the next module,
304
00:10:33,880 --> 00:10:35,710
we'll talk about recoverable errors
305
00:10:35,910 --> 00:10:38,080
and how to handle them using the types Result
306
00:10:38,280 --> 00:10:38,710
and Option.