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