@@ -83,7 +83,7 @@ fn main() {
83
83
}
84
84
```
85
85
86
- This has the same behavior as our previous example: If the call to ` open() `
86
+ This has similar behavior as our previous example: If the call to ` open() `
87
87
returns ` Ok ` , return the value inside. If it's an ` Err ` , panic.
88
88
89
89
There's also another method, similar to ` unwrap() ` , but that lets us choose the
@@ -97,7 +97,7 @@ a panic easier. `expect()` looks like this:
97
97
use std::fs::File;
98
98
99
99
fn main() {
100
- let f = File::open("hello.txt").expect("failed to open hello.txt");
100
+ let f = File::open("hello.txt").expect("Failed to open hello.txt. ");
101
101
}
102
102
```
103
103
@@ -109,55 +109,132 @@ turn an unrecoverable `panic!` into a recoverable one. This is why good Rust
109
109
code chooses to make errors recoverable: you give your caller choices.
110
110
111
111
The Rust community has a love/hate relationship with ` unwrap() ` and ` expect() ` .
112
- They're useful in tests since they will cause the test to fail if there's an
113
- error anyplace you call them. In examples, you might not want to muddy the code
114
- with proper error handling. But if you use them in a library, mis-using your
115
- library can cause other people's programs to halt unexpectedly, and that's not
116
- very user-friendly.
112
+ They're very handy when prototyping, before you're ready to decide how to
113
+ handle errors, and in that case they leave clear markers to look for when you
114
+ are ready to make your program more robust. They're useful in tests since they
115
+ will cause the test to fail if there's an error anyplace you call them. In
116
+ examples, you might not want to muddy the code with proper error handling. But
117
+ if you use them in a library, mis-using your library can cause other people's
118
+ programs to halt unexpectedly, and that's not very user-friendly.
117
119
118
120
## Propagating errors with ` ? `
119
121
120
122
When writing a function, if you don't want to handle the error where you are,
121
- you can return the error to the calling function. Within your function, that
122
- would look like:
123
+ you can return the error to the calling function. For example, here's a
124
+ function that reads a username from a file. If the file doesn't exist or can't
125
+ be read, this function will return those errors to the code that called this
126
+ function:
123
127
124
- <!-- I'll ghost everything except `return Err(e)` in the libreoffice file /Carol -->
125
-
126
- ``` rust,ignore
128
+ ``` rust
127
129
# use std :: fs :: File ;
128
- # fn foo() -> std::io::Result<()> {
129
- let f = File::open("hello.txt");
130
+ # use std :: io;
131
+ # use std :: io :: Read ;
132
+ #
133
+ fn read_username_from_file () -> Result <String , io :: Error > {
134
+ let f = File :: open (" hello.txt" );
135
+
136
+ let mut f = match f {
137
+ Ok (file ) => file ,
138
+ Err (e ) => return Err (e ),
139
+ };
130
140
131
- let f = match f {
132
- Ok(file) => file,
133
- Err(e) => return Err(e),
134
- };
141
+ let mut s = String :: new ();
135
142
136
- # Ok(())
137
- # }
143
+ match f . read_to_string (& mut s ) {
144
+ Ok (_ ) => Ok (s ),
145
+ Err (e ) => Err (e ),
146
+ }
147
+ }
138
148
```
139
149
140
150
This is a very common way of handling errors: propagate them upward until
141
151
you're ready to deal with them. This pattern is so common in Rust that there is
142
- dedicated syntax for it: the question mark operator. We could have also written
143
- the example like this:
152
+ a macro for it, ` try! ` , and as of Rust 1.XX, dedicated syntax for it: the
153
+ question mark operator. We could have written the above like this using the
154
+ ` try! ` macro and it would have the same functionality as the ` match ` expressions:
144
155
145
- <!-- I'll ghost everything except `?` in the libreoffice file /Carol -->
156
+ <!-- I'll ghost everything except the calls to `try!` in the libreoffice file
157
+ /Carol -->
146
158
147
- ``` rust,ignore
159
+ ``` rust
160
+ # use std :: fs :: File ;
161
+ # use std :: io;
162
+ # use std :: io :: Read ;
163
+ #
164
+ fn read_username_from_file () -> Result <String , io :: Error > {
165
+ let mut f = try ! (File :: open (" hello.txt" ));
166
+ let mut s = String :: new ();
167
+ try ! (f . read_to_string (& mut s ));
168
+ Ok (s )
169
+ }
170
+ ```
171
+
172
+ Or like this using the question mark operator:
173
+
174
+ <!-- I'll ghost everything except the question mark operator in the libreoffice
175
+ file. Also note the `#![feature(question_mark)]` line won't be needed once this
176
+ feature has made it into a stable version of Rust, which will happen well
177
+ before the book's publication. /Carol -->
178
+
179
+ ``` rust
180
+ #![feature(question_mark)]
181
+ # fn main () {}
182
+ # use std :: fs :: File ;
183
+ # use std :: io;
184
+ # use std :: io :: Read ;
185
+ #
186
+ fn read_username_from_file () -> Result <String , io :: Error > {
187
+ let mut f = File :: open (" hello.txt" )? ;
188
+ let mut s = String :: new ();
189
+ f . read_to_string (& mut s )? ;
190
+ Ok (s )
191
+ }
192
+ ```
193
+
194
+ The ` ? ` operator at the end of the ` open ` call does the same thing as the
195
+ example that uses ` match ` and the example that uses the ` try! ` macro: It will
196
+ return the value inside an ` Ok ` to the binding ` f ` , but will return early out
197
+ of the whole function and give any ` Err ` value we get to our caller. The same
198
+ thing applies to the ` ? ` at the end of the ` read_to_string ` call.
199
+
200
+ The advantage of using the question mark operator over the ` try! ` macro is the
201
+ question mark operator permits chaining. We could further shorten this code
202
+ by instead doing:
203
+
204
+ ``` rust
148
205
#![feature(question_mark)]
206
+ # fn main () {}
207
+ # use std :: fs :: File ;
208
+ # use std :: io;
209
+ # use std :: io :: Read ;
210
+ #
211
+ fn read_username_from_file () -> Result <String , io :: Error > {
212
+ let mut s = String :: new ();
213
+ File :: open (" hello.txt" )? . read_to_string (& mut s )? ;
214
+ Ok (s )
215
+ }
216
+ ```
149
217
150
- use std::fs::File;
218
+ Much nicer, right? The ` try! ` macro and the ` ? ` operator make propagating
219
+ errors upwards much more ergonomic. There's one catch though: they can only be
220
+ used in functions that return a ` Result ` , since they expand to the same ` match `
221
+ expression we saw above that had a potential early return of an ` Err ` value. Let's look at what happens if we try to use ` ? ` in the ` main ` function, which has a return type of ` () ` :
151
222
223
+ ``` rust,ignore
224
+ #![feature(question_mark)]
225
+ # use std::fs::File;
152
226
fn main() {
153
227
let f = File::open("hello.txt")?;
154
228
}
155
229
```
156
230
157
- The ` ? ` operator at the end of the ` open ` call does the same thing as our
158
- previous example: It will return the value inside an ` Ok ` to the binding ` f ` ,
159
- but will return early out of the whole function and give any ` Err ` value we get
160
- to our caller.
231
+ When we compile this, we get the following error message:
232
+
233
+ ``` bash
234
+ ```
235
+
236
+
237
+
161
238
162
239
There's one problem though; let's try compiling the example:
163
240
0 commit comments