1
00:00:00,420 --> 00:00:01,730
Welcome to module 4
2
00:00:02,240 --> 00:00:03,390
on returning Result
3
00:00:03,590 --> 00:00:04,740
and using ?.
4
00:00:06,550 --> 00:00:08,529
In this module, we're going to cover
5
00:00:08,729 --> 00:00:10,449
how to return Result values from
6
00:00:10,649 --> 00:00:11,860
your own functions that might fail.
7
00:00:12,370 --> 00:00:14,469
Then, we'll discuss how to use the ?
8
00:00:14,669 --> 00:00:16,328
operator within functions that return
9
00:00:16,528 --> 00:00:18,159
Result to propagate errors in
10
00:00:18,359 --> 00:00:19,000
a concise way.
11
00:00:19,540 --> 00:00:21,639
Understanding what the ? operator
12
00:00:21,839 --> 00:00:23,589
does will help you to understand where
13
00:00:23,789 --> 00:00:24,910
you can and can't use it.
14
00:00:25,420 --> 00:00:26,760
And we'll round out the module
15
00:00:26,960 --> 00:00:28,820
with some recent features added to Rust
16
00:00:29,120 --> 00:00:31,030
and make the ? operator useful
17
00:00:31,230 --> 00:00:32,180
in more contexts.
18
00:00:33,850 --> 00:00:35,800
Let's start with how to return a Result.
19
00:00:37,430 --> 00:00:39,510
Imagine we're implementing the back end
20
00:00:39,710 --> 00:00:41,729
of a Twitter-like website where people
21
00:00:41,929 --> 00:00:43,619
can post statuses containing up to
22
00:00:43,819 --> 00:00:45,330
200 bytes of text each.
23
00:00:46,850 --> 00:00:48,919
If a user of our site tries
24
00:00:49,119 --> 00:00:50,929
to send a status containing more
25
00:00:51,129 --> 00:00:52,490
than 200 bytes of text,
26
00:00:52,910 --> 00:00:54,230
we should return an error.
27
00:00:55,930 --> 00:00:57,700
We're going to write the part of this web app
28
00:00:57,900 --> 00:00:59,830
that's the function named save_status.
29
00:01:00,870 --> 00:01:03,210
This function takes the content of a status,
30
00:01:03,510 --> 00:01:05,440
checks that there are fewer than 200 bytes of
31
00:01:05,640 --> 00:01:07,650
text, and saves it to the database.
32
00:01:08,640 --> 00:01:10,260
If those operations succeed,
33
00:01:10,560 --> 00:01:12,809
we'll return the ID of the new status'
34
00:01:13,009 --> 00:01:13,820
database record.
35
00:01:14,370 --> 00:01:16,020
If those operations fail,
36
00:01:16,350 --> 00:01:18,209
we'll return an error describing what went
37
00:01:18,409 --> 00:01:18,570
wrong.
38
00:01:20,130 --> 00:01:22,080
The save_status function signature
39
00:01:22,380 --> 00:01:23,249
will have a parameter
40
00:01:23,449 --> 00:01:25,270
named text of type string
41
00:01:25,470 --> 00:01:25,830
slice.
42
00:01:27,380 --> 00:01:29,300
The return type will be a Result
43
00:01:29,810 --> 00:01:31,669
and then we have to specify the types that
44
00:01:31,869 --> 00:01:33,680
will be returned in the success case
45
00:01:33,880 --> 00:01:36,050
and the failure case within angle brackets.
46
00:01:37,630 --> 00:01:39,579
Because, in the success case, we want
47
00:01:39,779 --> 00:01:41,589
to return an ID, the first
48
00:01:41,789 --> 00:01:43,640
type in the angle brackets will be i64 -
49
00:01:43,840 --> 00:01:45,579
the type our database
50
00:01:45,779 --> 00:01:46,980
uses for the id field.
51
00:01:48,520 --> 00:01:50,589
For the error case, we're going to keep
52
00:01:50,789 --> 00:01:51,850
this example simple
53
00:01:52,240 --> 00:01:54,340
and return a hardcoded string literal
54
00:01:54,540 --> 00:01:55,900
that describes what went wrong.
55
00:01:56,470 --> 00:01:58,329
Therefore, we specify 'static str
56
00:01:58,529 --> 00:02:00,160
slice as the second type
57
00:02:00,360 --> 00:02:01,300
in the angle brackets.
58
00:02:02,390 --> 00:02:04,250
We'll be talking more about lifetimes
59
00:02:04,450 --> 00:02:05,120
in the next unit.
60
00:02:05,580 --> 00:02:07,459
All you need to know right now is
61
00:02:07,659 --> 00:02:09,349
that 'static means a reference
62
00:02:09,549 --> 00:02:11,269
that stays valid for the entire duration of
63
00:02:11,469 --> 00:02:13,099
the program, which hardcoded
64
00:02:13,299 --> 00:02:14,160
string literals are.
65
00:02:15,780 --> 00:02:17,130
In the body of the function,
66
00:02:17,400 --> 00:02:19,320
we want to test the number of bytes
67
00:02:19,520 --> 00:02:20,190
and the status,
68
00:02:20,680 --> 00:02:22,830
and if it's over our limit of 200,
69
00:02:23,310 --> 00:02:25,230
return the Err variant of the Result
70
00:02:25,430 --> 00:02:27,140
enum containing a hardcoded
71
00:02:27,340 --> 00:02:28,980
string describing the problem.
72
00:02:30,540 --> 00:02:32,010
To finish writing this function,
73
00:02:32,490 --> 00:02:34,349
let's look at a situation where we
74
00:02:34,549 --> 00:02:36,330
want to propagate an error that
75
00:02:36,530 --> 00:02:38,370
will illustrate how to use the ?
76
00:02:38,570 --> 00:02:38,960
operator.
77
00:02:40,500 --> 00:02:42,350
In the next part of the save_status
78
00:02:42,550 --> 00:02:42,930
function,
79
00:02:43,410 --> 00:02:45,239
we want to save the status text to the
80
00:02:45,439 --> 00:02:45,950
database.
81
00:02:46,500 --> 00:02:48,479
Imagine we have a function provided
82
00:02:48,679 --> 00:02:50,369
by a library for saving to
83
00:02:50,569 --> 00:02:52,440
the database that takes a string slice
84
00:02:52,770 --> 00:02:54,869
and returns a Result that will contain
85
00:02:55,069 --> 00:02:56,850
an instance of a StatusRecord struct
86
00:02:57,050 --> 00:02:58,830
if it succeeds and a hardcoded
87
00:02:59,030 --> 00:03:00,450
error message if it fails.
88
00:03:02,010 --> 00:03:03,890
In save_status, we're going to
89
00:03:04,090 --> 00:03:05,790
call the save_to_database function
90
00:03:06,120 --> 00:03:07,680
and handle the returned Result
91
00:03:07,880 --> 00:03:09,659
with a match expression, as we did in
92
00:03:09,859 --> 00:03:10,580
the previous module.
93
00:03:12,140 --> 00:03:13,460
If we match the Ok
94
00:03:13,660 --> 00:03:15,409
variant, we want to extract the
95
00:03:15,609 --> 00:03:16,520
inner status record
96
00:03:16,910 --> 00:03:18,620
and save it in the variable record.
97
00:03:20,170 --> 00:03:21,999
Once we have the record, we
98
00:03:22,199 --> 00:03:23,440
can get the id value
99
00:03:23,640 --> 00:03:25,720
and return that from save_status wrapped
100
00:03:25,920 --> 00:03:26,030
in an Ok.
101
00:03:27,990 --> 00:03:29,690
If save_to_database fails,
102
00:03:30,270 --> 00:03:31,259
we don't want to do anything
103
00:03:31,459 --> 00:03:33,119
with the Err; we just want
104
00:03:33,319 --> 00:03:34,979
to stop what we're doing in the save_status
105
00:03:35,179 --> 00:03:35,700
function
106
00:03:36,000 --> 00:03:37,170
and return early
107
00:03:37,370 --> 00:03:39,419
with the error for whatever code is calling
108
00:03:39,619 --> 00:03:40,710
save_status to handle.
109
00:03:41,810 --> 00:03:43,950
This pattern of extracting a success
110
00:03:44,150 --> 00:03:45,859
value and propagating an Err
111
00:03:46,059 --> 00:03:48,080
value is so common in Rust
112
00:03:48,410 --> 00:03:50,240
that the ? operator was added
113
00:03:50,440 --> 00:03:51,350
to have this behavior.
114
00:03:53,040 --> 00:03:55,140
We can replace this match expression
115
00:03:55,470 --> 00:03:57,490
with only a call to the save_to_database
116
00:03:57,690 --> 00:03:59,579
function followed by a ?
117
00:03:59,779 --> 00:03:59,970
,
118
00:04:00,540 --> 00:04:02,430
and this code will have the same behavior
119
00:04:02,630 --> 00:04:04,490
written in a much more concise way.
120
00:04:05,650 --> 00:04:07,659
There's a bit of automatic conversion
121
00:04:07,859 --> 00:04:09,920
that ? does for you as well,
122
00:04:10,520 --> 00:04:12,470
which we'll discuss in the future module
123
00:04:12,670 --> 00:04:13,650
on custom error types.
124
00:04:15,370 --> 00:04:17,229
Now the save_status function is
125
00:04:17,429 --> 00:04:17,680
complete
126
00:04:18,220 --> 00:04:20,179
and this code compiles successfully.
127
00:04:21,899 --> 00:04:24,089
As an aside, before the ?
128
00:04:24,289 --> 00:04:25,980
operator was added to the language,
129
00:04:26,340 --> 00:04:28,200
it was implemented as a macro called
130
00:04:28,400 --> 00:04:28,620
try!.
131
00:04:29,000 --> 00:04:30,570
If you're reading old code
132
00:04:30,770 --> 00:04:32,610
and see calls to the try! macro,
133
00:04:33,150 --> 00:04:34,860
know it serves the same purpose
134
00:04:35,060 --> 00:04:36,120
as ?.
135
00:04:37,610 --> 00:04:39,650
Now that we've covered what ?
136
00:04:39,850 --> 00:04:41,570
does, let's talk about
137
00:04:41,770 --> 00:04:43,220
where we can use ?.
138
00:04:44,730 --> 00:04:46,590
If we try to use ?
139
00:04:46,790 --> 00:04:47,790
in a function like this
140
00:04:47,990 --> 00:04:49,900
add_five function that returns
141
00:04:50,100 --> 00:04:50,810
i32,
142
00:04:51,230 --> 00:04:53,579
we'll get an error that says the `?`
143
00:04:53,779 --> 00:04:55,409
operator can only be used in
144
00:04:55,609 --> 00:04:57,150
a function that returns `Result`.
145
00:04:58,740 --> 00:05:00,810
This is because of the early return
146
00:05:01,010 --> 00:05:02,909
of an Err that the question mark
147
00:05:03,109 --> 00:05:04,859
does, as we saw in
148
00:05:05,059 --> 00:05:05,850
the last example.
149
00:05:07,360 --> 00:05:09,369
The signature of the function in which
150
00:05:09,569 --> 00:05:11,320
we use the ? operator
151
00:05:11,770 --> 00:05:13,750
needs to allow for the Err variant
152
00:05:13,950 --> 00:05:14,860
to be returned.
153
00:05:15,460 --> 00:05:17,230
So, it needs to have a return type
154
00:05:17,430 --> 00:05:19,329
that matches what we're using ?
155
00:05:19,529 --> 00:05:19,720
on.
156
00:05:21,280 --> 00:05:22,930
In the add_five example,
157
00:05:23,200 --> 00:05:25,390
if we wanted to be able to use ?
158
00:05:25,590 --> 00:05:27,549
here, we can change the return
159
00:05:27,749 --> 00:05:29,460
type to be Result
160
00:05:29,660 --> 00:05:31,240
ParseIntError
161
00:05:31,680 --> 00:05:33,790
and change what we return to be wrapped
162
00:05:33,990 --> 00:05:34,980
in an Ok variant.
163
00:05:36,570 --> 00:05:38,469
There have been some improvements to the ?
164
00:05:38,669 --> 00:05:39,810
operator recently.
165
00:05:40,290 --> 00:05:41,720
Let's talk about those briefly.
166
00:05:43,290 --> 00:05:45,300
When the ? was introduced,
167
00:05:45,500 --> 00:05:47,340
you could only use it on instances
168
00:05:47,540 --> 00:05:48,540
of the Result enum.
169
00:05:49,530 --> 00:05:51,180
As of Rust 1.22,
170
00:05:51,720 --> 00:05:53,909
you can use ? on Option values
171
00:05:54,109 --> 00:05:54,390
as well.
172
00:05:55,890 --> 00:05:57,979
Here's an example using ?
173
00:05:58,179 --> 00:05:58,780
on Option.
174
00:05:59,540 --> 00:06:01,580
The behavior of ? an Option
175
00:06:01,780 --> 00:06:03,510
is that it extracts a value from the
176
00:06:03,710 --> 00:06:04,370
Some variant
177
00:06:04,760 --> 00:06:06,480
and returns a None variant early,
178
00:06:06,680 --> 00:06:07,550
if it finds one.
179
00:06:09,110 --> 00:06:11,080
This function will return a Some
180
00:06:11,280 --> 00:06:13,010
variant containing the last number
181
00:06:13,210 --> 00:06:13,810
plus 5,
182
00:06:14,240 --> 00:06:16,270
if there is a last number in list.
183
00:06:16,910 --> 00:06:18,740
If there isn't, this function
184
00:06:18,940 --> 00:06:20,360
will return a None variant.
185
00:06:20,870 --> 00:06:22,490
The same restrictions apply.
186
00:06:23,150 --> 00:06:25,009
Using ? on an Option
187
00:06:25,209 --> 00:06:26,839
value must be done within
188
00:06:27,039 --> 00:06:28,820
a function that also returns Option.
189
00:06:30,380 --> 00:06:32,239
The next improvement was added in
190
00:06:32,439 --> 00:06:33,650
Rust 1.26 and
191
00:06:34,250 --> 00:06:36,319
enabled using ? to return
192
00:06:36,519 --> 00:06:37,730
Result values in main.
193
00:06:39,280 --> 00:06:41,320
The main function is a special function
194
00:06:41,520 --> 00:06:43,420
that is the entry point for a binary.
195
00:06:44,990 --> 00:06:46,790
Before Rust 1.26,
196
00:06:47,180 --> 00:06:48,890
main couldn't return a value.
197
00:06:49,400 --> 00:06:51,260
So, attempting to use ?
198
00:06:51,460 --> 00:06:53,240
in the main function would result
199
00:06:53,440 --> 00:06:54,100
in the same error
200
00:06:54,300 --> 00:06:56,089
we saw before about only
201
00:06:56,289 --> 00:06:57,920
being able to use ?
202
00:06:58,120 --> 00:06:59,570
in functions that return Result.
203
00:07:00,080 --> 00:07:02,090
There was no way of fixing this error
204
00:07:02,450 --> 00:07:04,190
other than using a match expression
205
00:07:04,400 --> 00:07:06,790
to handle the result instead of ?.
206
00:07:08,320 --> 00:07:09,670
In Rust 1.26
207
00:07:09,870 --> 00:07:11,930
and later, you can add a Result
208
00:07:12,130 --> 00:07:13,929
return type to main, plus
209
00:07:14,129 --> 00:07:15,789
return an Ok variant at the end
210
00:07:15,989 --> 00:07:16,300
of main
211
00:07:16,900 --> 00:07:18,990
and this enables the use of the ?
212
00:07:19,190 --> 00:07:20,860
operator in main on results
213
00:07:21,060 --> 00:07:21,280
only.
214
00:07:22,800 --> 00:07:23,940
In Rust 1.28
215
00:07:24,140 --> 00:07:25,949
and later, test functions have
216
00:07:26,149 --> 00:07:28,050
the ability to have a Result return type
217
00:07:28,250 --> 00:07:29,909
in a similar way so that you
218
00:07:30,109 --> 00:07:31,840
can use ? in tests too.
219
00:07:33,430 --> 00:07:34,360
In this module,
220
00:07:34,750 --> 00:07:36,699
we discussed how to return the Result
221
00:07:36,899 --> 00:07:38,589
type from your own functions by
222
00:07:38,789 --> 00:07:40,719
choosing and specifying a Success
223
00:07:40,919 --> 00:07:41,350
value type
224
00:07:41,560 --> 00:07:42,820
and an Error value type.
225
00:07:44,360 --> 00:07:46,190
We looked at the ? operator
226
00:07:46,390 --> 00:07:47,980
and how it propagates errors.
227
00:07:49,540 --> 00:07:51,519
We discussed how ? can only
228
00:07:51,719 --> 00:07:53,589
be used in functions that return the type
229
00:07:53,789 --> 00:07:54,880
? is called on,
230
00:07:55,480 --> 00:07:57,280
and we went over recent improvements
231
00:07:57,480 --> 00:07:59,020
that allow using the ?
232
00:07:59,350 --> 00:08:01,300
with the Option type, in main,
233
00:08:01,660 --> 00:08:02,620
and in tests.
234
00:08:04,170 --> 00:08:06,029
In the next module, we'll go
235
00:08:06,229 --> 00:08:07,970
over the advantages your programs
236
00:08:08,170 --> 00:08:10,480
gain from Rust's error handling strategy.