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