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