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