]>
Commit | Line | Data |
---|---|---|
83c7162d XL |
1 | ## Control Flow |
2 | ||
3 | Deciding whether or not to run some code depending on if a condition is true | |
4 | and deciding to run some code repeatedly while a condition is true are basic | |
5 | building blocks in most programming languages. The most common constructs that | |
6 | let you control the flow of execution of Rust code are `if` expressions and | |
7 | loops. | |
8 | ||
9 | ### `if` Expressions | |
10 | ||
11 | An `if` expression allows you to branch your code depending on conditions. You | |
12 | provide a condition and then state, “If this condition is met, run this block | |
13 | of code. If the condition is not met, do not run this block of code.” | |
14 | ||
15 | Create a new project called *branches* in your *projects* directory to explore | |
16 | the `if` expression. In the *src/main.rs* file, input the following: | |
17 | ||
18 | <span class="filename">Filename: src/main.rs</span> | |
19 | ||
20 | ```rust | |
21 | fn main() { | |
22 | let number = 3; | |
23 | ||
24 | if number < 5 { | |
25 | println!("condition was true"); | |
26 | } else { | |
27 | println!("condition was false"); | |
28 | } | |
29 | } | |
30 | ``` | |
31 | ||
32 | <!-- NEXT PARAGRAPH WRAPPED WEIRD INTENTIONALLY SEE #199 --> | |
33 | ||
34 | All `if` expressions start with the keyword `if`, which is followed by a | |
35 | condition. In this case, the condition checks whether or not the variable | |
36 | `number` has a value less than 5. The block of code we want to execute if the | |
37 | condition is true is placed immediately after the condition inside curly | |
38 | brackets. Blocks of code associated with the conditions in `if` expressions are | |
39 | sometimes called *arms*, just like the arms in `match` expressions that we | |
40 | discussed in the “Comparing the Guess to the Secret Number” section of | |
41 | Chapter 2. | |
42 | ||
43 | Optionally, we can also include an `else` expression, which we chose | |
44 | to do here, to give the program an alternative block of code to execute should | |
45 | the condition evaluate to false. If you don’t provide an `else` expression and | |
46 | the condition is false, the program will just skip the `if` block and move on | |
47 | to the next bit of code. | |
48 | ||
49 | Try running this code; you should see the following output: | |
50 | ||
51 | ```text | |
52 | $ cargo run | |
53 | Compiling branches v0.1.0 (file:///projects/branches) | |
54 | Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs | |
55 | Running `target/debug/branches` | |
56 | condition was true | |
57 | ``` | |
58 | ||
59 | Let’s try changing the value of `number` to a value that makes the condition | |
60 | `false` to see what happens: | |
61 | ||
62 | ```rust,ignore | |
63 | let number = 7; | |
64 | ``` | |
65 | ||
66 | Run the program again, and look at the output: | |
67 | ||
68 | ```text | |
69 | $ cargo run | |
70 | Compiling branches v0.1.0 (file:///projects/branches) | |
71 | Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs | |
72 | Running `target/debug/branches` | |
73 | condition was false | |
74 | ``` | |
75 | ||
76 | It’s also worth noting that the condition in this code *must* be a `bool`. If | |
77 | the condition isn’t a `bool`, we’ll get an error. For example: | |
78 | ||
79 | <span class="filename">Filename: src/main.rs</span> | |
80 | ||
81 | ```rust,ignore | |
82 | fn main() { | |
83 | let number = 3; | |
84 | ||
85 | if number { | |
86 | println!("number was three"); | |
87 | } | |
88 | } | |
89 | ``` | |
90 | ||
91 | The `if` condition evaluates to a value of `3` this time, and Rust throws an | |
92 | error: | |
93 | ||
94 | ```text | |
95 | error[E0308]: mismatched types | |
96 | --> src/main.rs:4:8 | |
97 | | | |
98 | 4 | if number { | |
99 | | ^^^^^^ expected bool, found integral variable | |
100 | | | |
101 | = note: expected type `bool` | |
102 | found type `{integer}` | |
103 | ``` | |
104 | ||
105 | The error indicates that Rust expected a `bool` but got an integer. Unlike | |
106 | languages such as Ruby and JavaScript, Rust will not automatically try to | |
107 | convert non-Boolean types to a Boolean. You must be explicit and always provide | |
108 | `if` with a Boolean as its condition. If we want the `if` code block to run | |
109 | only when a number is not equal to `0`, for example, we can change the `if` | |
110 | expression to the following: | |
111 | ||
112 | <span class="filename">Filename: src/main.rs</span> | |
113 | ||
114 | ```rust | |
115 | fn main() { | |
116 | let number = 3; | |
117 | ||
118 | if number != 0 { | |
119 | println!("number was something other than zero"); | |
120 | } | |
121 | } | |
122 | ``` | |
123 | ||
124 | Running this code will print `number was something other than zero`. | |
125 | ||
126 | #### Handling Multiple Conditions with `else if` | |
127 | ||
128 | You can have multiple conditions by combining `if` and `else` in an `else if` | |
129 | expression. For example: | |
130 | ||
131 | <span class="filename">Filename: src/main.rs</span> | |
132 | ||
133 | ```rust | |
134 | fn main() { | |
135 | let number = 6; | |
136 | ||
137 | if number % 4 == 0 { | |
138 | println!("number is divisible by 4"); | |
139 | } else if number % 3 == 0 { | |
140 | println!("number is divisible by 3"); | |
141 | } else if number % 2 == 0 { | |
142 | println!("number is divisible by 2"); | |
143 | } else { | |
144 | println!("number is not divisible by 4, 3, or 2"); | |
145 | } | |
146 | } | |
147 | ``` | |
148 | ||
149 | This program has four possible paths it can take. After running it, you should | |
150 | see the following output: | |
151 | ||
152 | ```text | |
153 | $ cargo run | |
154 | Compiling branches v0.1.0 (file:///projects/branches) | |
155 | Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs | |
156 | Running `target/debug/branches` | |
157 | number is divisible by 3 | |
158 | ``` | |
159 | ||
160 | When this program executes, it checks each `if` expression in turn and executes | |
161 | the first body for which the condition holds true. Note that even though 6 is | |
162 | divisible by 2, we don’t see the output `number is divisible by 2`, nor do we | |
163 | see the `number is not divisible by 4, 3, or 2` text from the `else` block. | |
164 | That’s because Rust only executes the block for the first true condition, and | |
165 | once it finds one, it doesn’t even check the rest. | |
166 | ||
167 | Using too many `else if` expressions can clutter your code, so if you have more | |
168 | than one, you might want to refactor your code. Chapter 6 describes a powerful | |
169 | Rust branching construct called `match` for these cases. | |
170 | ||
171 | #### Using `if` in a `let` Statement | |
172 | ||
173 | Because `if` is an expression, we can use it on the right side of a `let` | |
174 | statement, as in Listing 3-2: | |
175 | ||
176 | <span class="filename">Filename: src/main.rs</span> | |
177 | ||
178 | ```rust | |
179 | fn main() { | |
180 | let condition = true; | |
181 | let number = if condition { | |
182 | 5 | |
183 | } else { | |
184 | 6 | |
185 | }; | |
186 | ||
187 | println!("The value of number is: {}", number); | |
188 | } | |
189 | ``` | |
190 | ||
191 | <span class="caption">Listing 3-2: Assigning the result of an `if` expression | |
192 | to a variable</span> | |
193 | ||
194 | The `number` variable will be bound to a value based on the outcome of the `if` | |
195 | expression. Run this code to see what happens: | |
196 | ||
197 | ```text | |
198 | $ cargo run | |
199 | Compiling branches v0.1.0 (file:///projects/branches) | |
200 | Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs | |
201 | Running `target/debug/branches` | |
202 | The value of number is: 5 | |
203 | ``` | |
204 | ||
205 | Remember that blocks of code evaluate to the last expression in them, and | |
206 | numbers by themselves are also expressions. In this case, the value of the | |
207 | whole `if` expression depends on which block of code executes. This means the | |
208 | values that have the potential to be results from each arm of the `if` must be | |
209 | the same type; in Listing 3-2, the results of both the `if` arm and the `else` | |
210 | arm were `i32` integers. If the types are mismatched, as in the following | |
211 | example, we’ll get an error: | |
212 | ||
213 | <span class="filename">Filename: src/main.rs</span> | |
214 | ||
215 | ```rust,ignore | |
216 | fn main() { | |
217 | let condition = true; | |
218 | ||
219 | let number = if condition { | |
220 | 5 | |
221 | } else { | |
222 | "six" | |
223 | }; | |
224 | ||
225 | println!("The value of number is: {}", number); | |
226 | } | |
227 | ``` | |
228 | ||
94b46f34 | 229 | When we try to compile this code, we’ll get an error. The `if` and `else` arms have |
83c7162d XL |
230 | value types that are incompatible, and Rust indicates exactly where to find the |
231 | problem in the program: | |
232 | ||
233 | ```text | |
234 | error[E0308]: if and else have incompatible types | |
235 | --> src/main.rs:4:18 | |
236 | | | |
237 | 4 | let number = if condition { | |
238 | | __________________^ | |
239 | 5 | | 5 | |
240 | 6 | | } else { | |
241 | 7 | | "six" | |
242 | 8 | | }; | |
243 | | |_____^ expected integral variable, found &str | |
244 | | | |
245 | = note: expected type `{integer}` | |
246 | found type `&str` | |
247 | ``` | |
248 | ||
249 | The expression in the `if` block evaluates to an integer, and the expression in | |
250 | the `else` block evaluates to a string. This won’t work because variables must | |
251 | have a single type. Rust needs to know at compile time what type the `number` | |
252 | variable is, definitively, so it can verify at compile time that its type is | |
253 | valid everywhere we use `number`. Rust wouldn’t be able to do that if the type | |
254 | of `number` was only determined at runtime; the compiler would be more complex | |
255 | and would make fewer guarantees about the code if it had to keep track of | |
256 | multiple hypothetical types for any variable. | |
257 | ||
258 | ### Repetition with Loops | |
259 | ||
260 | It’s often useful to execute a block of code more than once. For this task, | |
261 | Rust provides several *loops*. A loop runs through the code inside the loop | |
262 | body to the end and then starts immediately back at the beginning. To | |
263 | experiment with loops, let’s make a new project called *loops*. | |
264 | ||
265 | Rust has three kinds of loops: `loop`, `while`, and `for`. Let’s try each one. | |
266 | ||
267 | #### Repeating Code with `loop` | |
268 | ||
269 | The `loop` keyword tells Rust to execute a block of code over and over again | |
270 | forever or until you explicitly tell it to stop. | |
271 | ||
272 | As an example, change the *src/main.rs* file in your *loops* directory to look | |
273 | like this: | |
274 | ||
275 | <span class="filename">Filename: src/main.rs</span> | |
276 | ||
277 | ```rust,ignore | |
278 | fn main() { | |
279 | loop { | |
280 | println!("again!"); | |
281 | } | |
282 | } | |
283 | ``` | |
284 | ||
285 | When we run this program, we’ll see `again!` printed over and over continuously | |
286 | until we stop the program manually. Most terminals support a keyboard shortcut, | |
287 | <span class="keystroke">ctrl-c</span>, to halt a program that is stuck in a | |
288 | continual loop. Give it a try: | |
289 | ||
290 | ```text | |
291 | $ cargo run | |
292 | Compiling loops v0.1.0 (file:///projects/loops) | |
293 | Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs | |
294 | Running `target/debug/loops` | |
295 | again! | |
296 | again! | |
297 | again! | |
298 | again! | |
299 | ^Cagain! | |
300 | ``` | |
301 | ||
302 | The symbol `^C` represents where you pressed <span class="keystroke">ctrl-c | |
303 | </span>. You may or may not see the word `again!` printed after the `^C`, | |
304 | depending on where the code was in the loop when it received the halt signal. | |
305 | ||
306 | Fortunately, Rust provides another, more reliable way to break out of a loop. | |
307 | You can place the `break` keyword within the loop to tell the program when to | |
308 | stop executing the loop. Recall that we did this in the guessing game in the | |
309 | “Quitting After a Correct Guess” section of Chapter 2 to exit the | |
310 | program when the user won the game by guessing the correct number. | |
311 | ||
94b46f34 XL |
312 | |
313 | #### Returning from loops | |
314 | ||
315 | One of the uses of a `loop` is to retry an operation you know can fail, such as | |
316 | checking if a thread completed its job. However, you might need to pass the | |
317 | result of that operation to the rest of your code. If you add it to the `break` | |
318 | expression you use to stop the loop, it will be returned by the broken loop: | |
319 | ||
320 | ```rust | |
321 | fn main() { | |
322 | let mut counter = 0; | |
323 | ||
324 | let result = loop { | |
325 | counter += 1; | |
326 | ||
327 | if counter == 10 { | |
328 | break counter * 2; | |
329 | } | |
330 | }; | |
331 | ||
332 | assert_eq!(result, 20); | |
333 | } | |
334 | ``` | |
335 | ||
83c7162d XL |
336 | #### Conditional Loops with `while` |
337 | ||
338 | It’s often useful for a program to evaluate a condition within a loop. While | |
339 | the condition is true, the loop runs. When the condition ceases to be true, the | |
340 | program calls `break`, stopping the loop. This loop type could be implemented | |
341 | using a combination of `loop`, `if`, `else`, and `break`; you could try that | |
342 | now in a program, if you’d like. | |
343 | ||
344 | However, this pattern is so common that Rust has a built-in language construct | |
345 | for it, called a `while` loop. Listing 3-3 uses `while`: the program loops | |
346 | three times, counting down each time, and then, after the loop, it prints | |
347 | another message and exits. | |
348 | ||
349 | <span class="filename">Filename: src/main.rs</span> | |
350 | ||
351 | ```rust | |
352 | fn main() { | |
353 | let mut number = 3; | |
354 | ||
355 | while number != 0 { | |
356 | println!("{}!", number); | |
357 | ||
358 | number = number - 1; | |
359 | } | |
360 | ||
361 | println!("LIFTOFF!!!"); | |
362 | } | |
363 | ``` | |
364 | ||
365 | <span class="caption">Listing 3-3: Using a `while` loop to run code while a | |
366 | condition holds true</span> | |
367 | ||
368 | This construct eliminates a lot of nesting that would be necessary if you used | |
369 | `loop`, `if`, `else`, and `break`, and it’s clearer. While a condition holds | |
370 | true, the code runs; otherwise, it exits the loop. | |
371 | ||
372 | #### Looping Through a Collection with `for` | |
373 | ||
374 | You could use the `while` construct to loop over the elements of a collection, | |
375 | such as an array. For example, let’s look at Listing 3-4: | |
376 | ||
377 | <span class="filename">Filename: src/main.rs</span> | |
378 | ||
379 | ```rust | |
380 | fn main() { | |
381 | let a = [10, 20, 30, 40, 50]; | |
382 | let mut index = 0; | |
383 | ||
384 | while index < 5 { | |
385 | println!("the value is: {}", a[index]); | |
386 | ||
387 | index = index + 1; | |
388 | } | |
389 | } | |
390 | ``` | |
391 | ||
392 | <span class="caption">Listing 3-4: Looping through each element of a collection | |
393 | using a `while` loop</span> | |
394 | ||
395 | Here, the code counts up through the elements in the array. It starts at index | |
396 | `0`, and then loops until it reaches the final index in the array (that is, | |
397 | when `index < 5` is no longer true). Running this code will print every element | |
398 | in the array: | |
399 | ||
400 | ```text | |
401 | $ cargo run | |
402 | Compiling loops v0.1.0 (file:///projects/loops) | |
403 | Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs | |
404 | Running `target/debug/loops` | |
405 | the value is: 10 | |
406 | the value is: 20 | |
407 | the value is: 30 | |
408 | the value is: 40 | |
409 | the value is: 50 | |
410 | ``` | |
411 | ||
412 | All five array values appear in the terminal, as expected. Even though `index` | |
413 | will reach a value of `5` at some point, the loop stops executing before trying | |
414 | to fetch a sixth value from the array. | |
415 | ||
416 | But this approach is error prone; we could cause the program to panic if the | |
417 | index length is incorrect. It’s also slow, because the compiler adds runtime | |
418 | code to perform the conditional check on every element on every iteration | |
419 | through the loop. | |
420 | ||
421 | As a more concise alternative, you can use a `for` loop and execute some code | |
422 | for each item in a collection. A `for` loop looks like this code in Listing 3-5: | |
423 | ||
424 | <span class="filename">Filename: src/main.rs</span> | |
425 | ||
426 | ```rust | |
427 | fn main() { | |
428 | let a = [10, 20, 30, 40, 50]; | |
429 | ||
430 | for element in a.iter() { | |
431 | println!("the value is: {}", element); | |
432 | } | |
433 | } | |
434 | ``` | |
435 | ||
436 | <span class="caption">Listing 3-5: Looping through each element of a collection | |
437 | using a `for` loop</span> | |
438 | ||
439 | When we run this code, we’ll see the same output as in Listing 3-4. More | |
440 | importantly, we’ve now increased the safety of the code and eliminated the | |
441 | chance of bugs that might result from going beyond the end of the array or not | |
442 | going far enough and missing some items. | |
443 | ||
444 | For example, in the code in Listing 3-4, if you removed an item from the `a` | |
445 | array but forgot to update the condition to `while index < 4`, the code would | |
446 | panic. Using the `for` loop, you wouldn’t need to remember to change any other | |
447 | code if you changed the number of values in the array. | |
448 | ||
449 | The safety and conciseness of `for` loops make them the most commonly used loop | |
450 | construct in Rust. Even in situations in which you want to run some code a | |
451 | certain number of times, as in the countdown example that used a `while` loop | |
452 | in Listing 3-3, most Rustaceans would use a `for` loop. The way to do that | |
453 | would be to use a `Range`, which is a type provided by the standard library | |
454 | that generates all numbers in sequence starting from one number and ending | |
455 | before another number. | |
456 | ||
457 | Here’s what the countdown would look like using a `for` loop and another method | |
458 | we’ve not yet talked about, `rev`, to reverse the range: | |
459 | ||
460 | <span class="filename">Filename: src/main.rs</span> | |
461 | ||
462 | ```rust | |
463 | fn main() { | |
464 | for number in (1..4).rev() { | |
465 | println!("{}!", number); | |
466 | } | |
467 | println!("LIFTOFF!!!"); | |
468 | } | |
469 | ``` | |
470 | ||
471 | This code is a bit nicer, isn’t it? | |
472 | ||
473 | ## Summary | |
474 | ||
475 | You made it! That was a sizable chapter: you learned about variables, scalar | |
476 | and compound data types, functions, comments, `if` expressions, and loops! If | |
477 | you want to practice with the concepts discussed in this chapter, try building | |
478 | programs to do the following: | |
479 | ||
480 | * Convert temperatures between Fahrenheit and Celsius. | |
481 | * Generate the nth Fibonacci number. | |
482 | * Print the lyrics to the Christmas carol “The Twelve Days of Christmas,” | |
483 | taking advantage of the repetition in the song. | |
484 | ||
485 | When you’re ready to move on, we’ll talk about a concept in Rust that *doesn’t* | |
486 | commonly exist in other programming languages: ownership. |