]>
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 | ||
42 | ```text | |
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 | ||
55 | ```text | |
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 | ||
72 | ```text | |
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 | ||
105 | ```text | |
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 | ||
137 | ```text | |
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 XL |
158 | |
159 | ```text | |
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 | ||
13cf67c4 XL |
206 | ```text |
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 XL |
222 | |
223 | Fortunately, Rust provides another, more reliable way to break out of a loop. | |
224 | You can place the `break` keyword within the loop to tell the program when to | |
225 | stop 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 | |
532ac7d7 | 230 | #### Returning Values from Loops |
13cf67c4 | 231 | |
532ac7d7 XL |
232 | One of the uses of a `loop` is to retry an operation you know might fail, such |
233 | as checking whether a thread has completed its job. However, you might need to | |
234 | pass the result of that operation to the rest of your code. To do this, you can | |
235 | add the value you want returned after the `break` expression you use to stop | |
236 | the loop; that value will be returned out of the loop so you can use it, as | |
237 | shown here: | |
13cf67c4 XL |
238 | |
239 | ```rust | |
74b04a01 | 240 | {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-33-return-value-from-loop/src/main.rs}} |
13cf67c4 XL |
241 | ``` |
242 | ||
9fa01778 | 243 | Before the loop, we declare a variable named `counter` and initialize it to |
532ac7d7 XL |
244 | `0`. Then we declare a variable named `result` to hold the value returned from |
245 | the loop. On every iteration of the loop, we add `1` to the `counter` variable, | |
246 | and then check whether the counter is equal to `10`. When it is, we use the | |
247 | `break` keyword with the value `counter * 2`. After the loop, we use a | |
248 | semicolon to end the statement that assigns the value to `result`. Finally, we | |
249 | print the value in `result`, which in this case is 20. | |
9fa01778 | 250 | |
13cf67c4 XL |
251 | #### Conditional Loops with `while` |
252 | ||
253 | It’s often useful for a program to evaluate a condition within a loop. While | |
254 | the condition is true, the loop runs. When the condition ceases to be true, the | |
255 | program calls `break`, stopping the loop. This loop type could be implemented | |
256 | using a combination of `loop`, `if`, `else`, and `break`; you could try that | |
257 | now in a program, if you’d like. | |
258 | ||
259 | However, this pattern is so common that Rust has a built-in language construct | |
260 | for it, called a `while` loop. Listing 3-3 uses `while`: the program loops | |
261 | three times, counting down each time, and then, after the loop, it prints | |
262 | another message and exits. | |
263 | ||
264 | <span class="filename">Filename: src/main.rs</span> | |
265 | ||
266 | ```rust | |
74b04a01 | 267 | {{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-03/src/main.rs}} |
13cf67c4 XL |
268 | ``` |
269 | ||
270 | <span class="caption">Listing 3-3: Using a `while` loop to run code while a | |
271 | condition holds true</span> | |
272 | ||
273 | This construct eliminates a lot of nesting that would be necessary if you used | |
274 | `loop`, `if`, `else`, and `break`, and it’s clearer. While a condition holds | |
275 | true, the code runs; otherwise, it exits the loop. | |
276 | ||
277 | #### Looping Through a Collection with `for` | |
278 | ||
279 | You could use the `while` construct to loop over the elements of a collection, | |
69743fb6 | 280 | such as an array. For example, let’s look at Listing 3-4. |
13cf67c4 XL |
281 | |
282 | <span class="filename">Filename: src/main.rs</span> | |
283 | ||
284 | ```rust | |
74b04a01 | 285 | {{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-04/src/main.rs}} |
13cf67c4 XL |
286 | ``` |
287 | ||
288 | <span class="caption">Listing 3-4: Looping through each element of a collection | |
289 | using a `while` loop</span> | |
290 | ||
291 | Here, the code counts up through the elements in the array. It starts at index | |
292 | `0`, and then loops until it reaches the final index in the array (that is, | |
293 | when `index < 5` is no longer true). Running this code will print every element | |
294 | in the array: | |
295 | ||
296 | ```text | |
74b04a01 | 297 | {{#include ../listings/ch03-common-programming-concepts/listing-03-04/output.txt}} |
13cf67c4 XL |
298 | ``` |
299 | ||
300 | All five array values appear in the terminal, as expected. Even though `index` | |
301 | will reach a value of `5` at some point, the loop stops executing before trying | |
302 | to fetch a sixth value from the array. | |
303 | ||
304 | But this approach is error prone; we could cause the program to panic if the | |
305 | index length is incorrect. It’s also slow, because the compiler adds runtime | |
306 | code to perform the conditional check on every element on every iteration | |
307 | through the loop. | |
308 | ||
309 | As a more concise alternative, you can use a `for` loop and execute some code | |
69743fb6 | 310 | for each item in a collection. A `for` loop looks like the code in Listing 3-5. |
13cf67c4 XL |
311 | |
312 | <span class="filename">Filename: src/main.rs</span> | |
313 | ||
314 | ```rust | |
74b04a01 | 315 | {{#rustdoc_include ../listings/ch03-common-programming-concepts/listing-03-05/src/main.rs}} |
13cf67c4 XL |
316 | ``` |
317 | ||
318 | <span class="caption">Listing 3-5: Looping through each element of a collection | |
319 | using a `for` loop</span> | |
320 | ||
321 | When we run this code, we’ll see the same output as in Listing 3-4. More | |
322 | importantly, we’ve now increased the safety of the code and eliminated the | |
323 | chance of bugs that might result from going beyond the end of the array or not | |
324 | going far enough and missing some items. | |
325 | ||
74b04a01 XL |
326 | For example, in the code in Listing 3-4, if you changed the definition of the |
327 | `a` array to have four elements but forgot to update the condition to `while | |
328 | index < 4`, the code would panic. Using the `for` loop, you wouldn’t need to | |
329 | remember to change any other code if you changed the number of values in the | |
330 | array. | |
13cf67c4 XL |
331 | |
332 | The safety and conciseness of `for` loops make them the most commonly used loop | |
333 | construct in Rust. Even in situations in which you want to run some code a | |
334 | certain number of times, as in the countdown example that used a `while` loop | |
335 | in Listing 3-3, most Rustaceans would use a `for` loop. The way to do that | |
336 | would be to use a `Range`, which is a type provided by the standard library | |
337 | that generates all numbers in sequence starting from one number and ending | |
338 | before another number. | |
339 | ||
340 | Here’s what the countdown would look like using a `for` loop and another method | |
341 | we’ve not yet talked about, `rev`, to reverse the range: | |
342 | ||
343 | <span class="filename">Filename: src/main.rs</span> | |
344 | ||
345 | ```rust | |
74b04a01 | 346 | {{#rustdoc_include ../listings/ch03-common-programming-concepts/no-listing-34-for-range/src/main.rs}} |
13cf67c4 XL |
347 | ``` |
348 | ||
349 | This code is a bit nicer, isn’t it? | |
350 | ||
351 | ## Summary | |
352 | ||
353 | You made it! That was a sizable chapter: you learned about variables, scalar | |
354 | and compound data types, functions, comments, `if` expressions, and loops! If | |
355 | you want to practice with the concepts discussed in this chapter, try building | |
356 | programs to do the following: | |
357 | ||
358 | * Convert temperatures between Fahrenheit and Celsius. | |
359 | * Generate the nth Fibonacci number. | |
360 | * Print the lyrics to the Christmas carol “The Twelve Days of Christmas,” | |
69743fb6 | 361 | taking advantage of the repetition in the song. |
13cf67c4 XL |
362 | |
363 | When you’re ready to move on, we’ll talk about a concept in Rust that *doesn’t* | |
364 | commonly exist in other programming languages: ownership. | |
9fa01778 XL |
365 | |
366 | [comparing-the-guess-to-the-secret-number]: | |
367 | ch02-00-guessing-game-tutorial.html#comparing-the-guess-to-the-secret-number | |
368 | [quitting-after-a-correct-guess]: | |
369 | ch02-00-guessing-game-tutorial.html#quitting-after-a-correct-guess |