]> git.proxmox.com Git - rustc.git/blob - src/doc/book/second-edition/src/ch03-05-control-flow.md
New upstream version 1.23.0+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch03-05-control-flow.md
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
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. 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)
52 Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
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)
69 Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
70 Running `target/debug/branches`
71 condition was false
72 ```
73
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:
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
105 not automatically try to convert non-boolean types to a boolean, unlike
106 languages such as Ruby and JavaScript. You must be explicit and always provide
107 `if` with a `boolean` as its condition. If we want the `if` code block to run
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)
154 Finished dev [unoptimized + debuginfo] target(s) in 0.31 secs
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`
173 statement, for instance in Listing 3-2:
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
190 <span class="caption">Listing 3-2: Assigning the result of an `if` expression
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)
199 Finished dev [unoptimized + debuginfo] target(s) in 0.30 secs
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
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:
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 {
237 | __________________^
238 5 | | 5
239 6 | | } else {
240 7 | | "six"
241 8 | | };
242 | |_____^ expected integral variable, found reference
243 |
244 = note: expected type `{integer}`
245 found type `&str`
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,
286 <span class="keystroke">ctrl-C</span>, to halt a program that is stuck in a
287 continual loop. Give it a try:
288
289 ```text
290 $ cargo run
291 Compiling loops v0.1.0 (file:///projects/loops)
292 Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs
293 Running `target/debug/loops`
294 again!
295 again!
296 again!
297 again!
298 ^Cagain!
299 ```
300
301 The symbol `^C` represents where you pressed <span class="keystroke">ctrl-C
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.
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,
347 such as an array. For example, let’s look at Listing 3-3:
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
364 <span class="caption">Listing 3-3: Looping through each element of a collection
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)
375 Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs
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
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:
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
408 <span class="caption">Listing 3-4: Looping through each element of a collection
409 using a `for` loop</span>
410
411 When we run this code, we’ll see the same output as in Listing 3-3. More
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
416 For example, in the code in Listing 3-3, if you removed an item from the `a`
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
424 in Listing 3-3, most Rustaceans would use a `for` loop. The way to do that
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
448 and `if` expressions, and loops! If you want to practice with the concepts
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.