|
4 | 4 |
|
5 | 5 |  for `Option<T>`
|
6 | 6 |
|
7 |
| - for using `?` in `main` and tests |
8 |
| - |
9 | 7 | Rust has gained a new operator, `?`, that makes error handling more pleasant
|
10 | 8 | by reducing the visual noise involved. It does this by solving one simple
|
11 | 9 | problem. To illustrate, imagine we had some code to read some data from a
|
@@ -120,131 +118,3 @@ case, `?` will return a value for `Some(T)` and return `None` for `None`. One
|
120 | 118 | current restriction is that you cannot use `?` for both in the same function,
|
121 | 119 | as the return type needs to match the type you use `?` on. In the future,
|
122 | 120 | this restriction will be lifed.
|
123 |
| - |
124 |
| -## `?` in `main` and tests |
125 |
| - |
126 |
| -Rust's error handling revolves around returning `Result<T, E>` and using `?` |
127 |
| -to propagate errors. For those who write many small programs and, hopefully, |
128 |
| -many tests, one common paper cut has been mixing entry points such as `main` |
129 |
| -and `#[test]`s with error handling. |
130 |
| - |
131 |
| -As an example, you might have tried to write: |
132 |
| - |
133 |
| -```rust,ignore |
134 |
| -use std::fs::File; |
135 |
| -
|
136 |
| -fn main() { |
137 |
| - let f = File::open("bar.txt")?; |
138 |
| -} |
139 |
| -``` |
140 |
| - |
141 |
| -Since `?` works by propagating the `Result` with an early return to the |
142 |
| -enclosing function, the snippet above does not work, and results today |
143 |
| -in the following error: |
144 |
| - |
145 |
| -```rust,ignore |
146 |
| -error[E0277]: the `?` operator can only be used in a function that returns `Result` |
147 |
| - or `Option` (or another type that implements `std::ops::Try`) |
148 |
| - --> src/main.rs:5:13 |
149 |
| - | |
150 |
| -5 | let f = File::open("bar.txt")?; |
151 |
| - | ^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()` |
152 |
| - | |
153 |
| - = help: the trait `std::ops::Try` is not implemented for `()` |
154 |
| - = note: required by `std::ops::Try::from_error` |
155 |
| -``` |
156 |
| - |
157 |
| -To solve this problem in Rust 2015, you might have written something like: |
158 |
| - |
159 |
| -```rust |
160 |
| -// Rust 2015 |
161 |
| - |
162 |
| -# use std::process; |
163 |
| -# use std::error::Error; |
164 |
| - |
165 |
| -fn run() -> Result<(), Box<Error>> { |
166 |
| - // real logic.. |
167 |
| - Ok(()) |
168 |
| -} |
169 |
| - |
170 |
| -fn main() { |
171 |
| - if let Err(e) = run() { |
172 |
| - println!("Application error: {}", e); |
173 |
| - process::exit(1); |
174 |
| - } |
175 |
| -} |
176 |
| -``` |
177 |
| - |
178 |
| -However, in this case, the `run` function has all the interesting logic and |
179 |
| -`main` is just boilerplate. The problem is even worse for `#[test]`s, since |
180 |
| -there tend to be a lot more of them. |
181 |
| - |
182 |
| -In Rust 2018 you can instead let your `#[test]`s and `main` functions return |
183 |
| -a `Result`: |
184 |
| - |
185 |
| -```rust,no_run |
186 |
| -// Rust 2018 |
187 |
| -
|
188 |
| -use std::fs::File; |
189 |
| -
|
190 |
| -fn main() -> Result<(), std::io::Error> { |
191 |
| - let f = File::open("bar.txt")?; |
192 |
| -
|
193 |
| - Ok(()) |
194 |
| -} |
195 |
| -``` |
196 |
| - |
197 |
| -In this case, if say the file doesn't exist and there is an `Err(err)` somewhere, |
198 |
| -then `main` will exit with an error code (not `0`) and print out a `Debug` |
199 |
| -representation of `err`. |
200 |
| - |
201 |
| -## More details |
202 |
| - |
203 |
| -Getting `-> Result<..>` to work in the context of `main` and `#[test]`s is not |
204 |
| -magic. It is all backed up by a `Termination` trait which all valid return |
205 |
| -types of `main` and testing functions must implement. The trait is defined as: |
206 |
| - |
207 |
| -```rust |
208 |
| -pub trait Termination { |
209 |
| - fn report(self) -> i32; |
210 |
| -} |
211 |
| -``` |
212 |
| - |
213 |
| -When setting up the entry point for your application, the compiler will use this |
214 |
| -trait and call `.report()` on the `Result` of the `main` function you have written. |
215 |
| - |
216 |
| -Two simplified example implementations of this trait for `Result` and `()` are: |
217 |
| - |
218 |
| -```rust |
219 |
| -# #![feature(process_exitcode_placeholder, termination_trait_lib)] |
220 |
| -# use std::process::ExitCode; |
221 |
| -# use std::fmt; |
222 |
| -# |
223 |
| -# pub trait Termination { fn report(self) -> i32; } |
224 |
| - |
225 |
| -impl Termination for () { |
226 |
| - fn report(self) -> i32 { |
227 |
| - # use std::process::Termination; |
228 |
| - ExitCode::SUCCESS.report() |
229 |
| - } |
230 |
| -} |
231 |
| - |
232 |
| -impl<E: fmt::Debug> Termination for Result<(), E> { |
233 |
| - fn report(self) -> i32 { |
234 |
| - match self { |
235 |
| - Ok(()) => ().report(), |
236 |
| - Err(err) => { |
237 |
| - eprintln!("Error: {:?}", err); |
238 |
| - # use std::process::Termination; |
239 |
| - ExitCode::FAILURE.report() |
240 |
| - } |
241 |
| - } |
242 |
| - } |
243 |
| -} |
244 |
| -``` |
245 |
| - |
246 |
| -As you can see in the case of `()`, a success code is simply returned. |
247 |
| -In the case of `Result`, the success case delegates to the implementation for |
248 |
| -`()` but prints out an error message and a failure exit code on `Err(..)`. |
249 |
| - |
250 |
| -To learn more about the finer details, consult either [the tracking issue](https://github.com/rust-lang/rust/issues/43301) or [the RFC](https://github.com/rust-lang/rfcs/blob/master/text/1937-ques-in-main.md). |
0 commit comments