]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch03-05-control-flow.md
New upstream version 1.25.0+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch03-05-control-flow.md
CommitLineData
cc61c64b
XL
1## Control Flow
2
3Deciding whether or not to run some code depending on if a condition is true or
4deciding 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 us to branch our code depending on conditions. We
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
ea8adc8c 38brackets. Blocks of code associated with the conditions in `if` expressions are
cc61c64b
XL
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. Optionally, we can also include an `else` expression, which we chose
42to do here, to give the program an alternative block of code to execute should
43the condition evaluate to false. If you don’t provide an `else` expression and
44the condition is false, the program will just skip the `if` block and move on
45to the next bit of code.
46
47Try 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`
54condition was true
55```
56
57Let’s try changing the value of `number` to a value that makes the condition
58`false` to see what happens:
59
60```rust,ignore
61let number = 7;
62```
63
64Run 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`
71condition was false
72```
73
abe05a73
XL
74It’s also worth noting that the condition in this code *must* be a `bool`. If
75the condition isn’t a `bool`, we’ll get an error. For example, try running the
76following code:
cc61c64b
XL
77
78<span class="filename">Filename: src/main.rs</span>
79
80```rust,ignore
81fn main() {
82 let number = 3;
83
84 if number {
85 println!("number was three");
86 }
87}
88```
89
90The `if` condition evaluates to a value of `3` this time, and Rust throws an
91error:
92
93```text
94error[E0308]: mismatched types
95 --> src/main.rs:4:8
96 |
974 | if number {
98 | ^^^^^^ expected bool, found integral variable
99 |
100 = note: expected type `bool`
101 found type `{integer}`
102```
103
104The error indicates that Rust expected a `bool` but got an integer. Rust will
ff7c6d11 105not automatically try to convert non-Boolean types to a Boolean, unlike
cc61c64b 106languages 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
108only when a number is not equal to `0`, for example, we can change the `if`
109expression to the following:
110
111<span class="filename">Filename: src/main.rs</span>
112
113```rust
114fn main() {
115 let number = 3;
116
117 if number != 0 {
118 println!("number was something other than zero");
119 }
120}
121```
122
123Running this code will print `number was something other than zero`.
124
125#### Multiple Conditions with `else if`
126
127We can have multiple conditions by combining `if` and `else` in an `else if`
128expression. For example:
129
130<span class="filename">Filename: src/main.rs</span>
131
132```rust
133fn 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
148This program has four possible paths it can take. After running it, you should
149see 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`
156number is divisible by 3
157```
158
159When this program executes, it checks each `if` expression in turn and executes
160the first body for which the condition holds true. Note that even though 6 is
161divisible by 2, we don’t see the output `number is divisible by 2`, nor do we
162see the `number is not divisible by 4, 3, or 2` text from the `else` block. The
163reason is that Rust will only execute the block for the first true condition,
164and once it finds one, it won’t even check the rest.
165
166Using too many `else if` expressions can clutter your code, so if you have more
167than one, you might want to refactor your code. Chapter 6 describes a powerful
168Rust branching construct called `match` for these cases.
169
170#### Using `if` in a `let` statement
171
172Because `if` is an expression, we can use it on the right side of a `let`
abe05a73 173statement, for instance in Listing 3-2:
cc61c64b
XL
174
175<span class="filename">Filename: src/main.rs</span>
176
177```rust
178fn 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
191to a variable</span>
192
193The `number` variable will be bound to a value based on the outcome of the `if`
194expression. 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`
201The value of number is: 5
202```
203
204Remember that blocks of code evaluate to the last expression in them, and
205numbers by themselves are also expressions. In this case, the value of the
206whole `if` expression depends on which block of code executes. This means the
207values that have the potential to be results from each arm of the `if` must be
abe05a73
XL
208the same type; in Listing 3-2, the results of both the `if` arm and the `else`
209arm were `i32` integers. If the types are mismatched, as in the following
210example, we’ll get an error:
cc61c64b
XL
211
212<span class="filename">Filename: src/main.rs</span>
213
214```rust,ignore
215fn 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
228When we try to run this code, we’ll get an error. The `if` and `else` arms have
229value types that are incompatible, and Rust indicates exactly where to find the
230problem in the program:
231
232```text
233error[E0308]: if and else have incompatible types
234 --> src/main.rs:4:18
235 |
2364 | let number = if condition {
7cac9316 237 | __________________^
cc61c64b
XL
2385 | | 5
2396 | | } else {
2407 | | "six"
2418 | | };
7cac9316 242 | |_____^ expected integral variable, found reference
cc61c64b
XL
243 |
244 = note: expected type `{integer}`
ea8adc8c 245 found type `&str`
cc61c64b
XL
246```
247
248The expression in the `if` block evaluates to an integer, and the expression in
249the `else` block evaluates to a string. This won’t work because variables must
250have a single type. Rust needs to know at compile time what type the `number`
251variable is, definitively, so it can verify at compile time that its type is
252valid everywhere we use `number`. Rust wouldn’t be able to do that if the type
253of `number` was only determined at runtime; the compiler would be more complex
254and would make fewer guarantees about the code if it had to keep track of
255multiple hypothetical types for any variable.
256
257### Repetition with Loops
258
259It’s often useful to execute a block of code more than once. For this task,
260Rust provides several *loops*. A loop runs through the code inside the loop
261body to the end and then starts immediately back at the beginning. To
262experiment with loops, let’s make a new project called *loops*.
263
264Rust has three kinds of loops: `loop`, `while`, and `for`. Let’s try each one.
265
266#### Repeating Code with `loop`
267
268The `loop` keyword tells Rust to execute a block of code over and over again
269forever or until you explicitly tell it to stop.
270
271As an example, change the *src/main.rs* file in your *loops* directory to look
272like this:
273
274<span class="filename">Filename: src/main.rs</span>
275
276```rust,ignore
277fn main() {
278 loop {
279 println!("again!");
280 }
281}
282```
283
284When we run this program, we’ll see `again!` printed over and over continuously
285until 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 287continual 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`
294again!
295again!
296again!
297again!
298^Cagain!
299```
300
2c00a5a8 301The 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`,
303depending on where the code was in the loop when it received the halt signal.
cc61c64b
XL
304
305Fortunately, Rust provides another, more reliable way to break out of a loop.
306You can place the `break` keyword within the loop to tell the program when to
307stop 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
309program when the user won the game by guessing the correct number.
310
311#### Conditional Loops with `while`
312
313It’s often useful for a program to evaluate a condition within a loop. While
314the condition is true, the loop runs. When the condition ceases to be true, you
315call `break`, stopping the loop. This loop type could be implemented using a
316combination of `loop`, `if`, `else`, and `break`; you could try that now in a
317program, if you’d like.
318
319However, this pattern is so common that Rust has a built-in language construct
320for it, and it’s called a `while` loop. The following example uses `while`: the
321program loops three times, counting down each time. Then, after the loop, it
322prints another message and exits:
323
324<span class="filename">Filename: src/main.rs</span>
325
326```rust
327fn 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
340This 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
342true, the code runs; otherwise, it exits the loop.
343
344#### Looping Through a Collection with `for`
345
346You could use the `while` construct to loop over the elements of a collection,
abe05a73 347such 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
352fn 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
365using a `while` loop</span>
366
367Here, 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,
369when `index < 5` is no longer true). Running this code will print out every
370element 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`
377the value is: 10
378the value is: 20
379the value is: 30
380the value is: 40
381the value is: 50
382```
383
384All five array values appear in the terminal, as expected. Even though `index`
385will reach a value of `5` at some point, the loop stops executing before trying
386to fetch a sixth value from the array.
387
388But this approach is error prone; we could cause the program to panic if the
389index length is incorrect. It’s also slow, because the compiler adds runtime
390code to perform the conditional check on every element on every iteration
391through the loop.
392
abe05a73
XL
393As a more concise alternative, you can use a `for` loop and execute some code
394for 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
399fn 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
409using a `for` loop</span>
410
abe05a73 411When we run this code, we’ll see the same output as in Listing 3-3. More
cc61c64b
XL
412importantly, we’ve now increased the safety of the code and eliminated the
413chance of bugs that might result from going beyond the end of the array or not
414going far enough and missing some items.
415
abe05a73 416For example, in the code in Listing 3-3, if you removed an item from the `a`
cc61c64b
XL
417array but forgot to update the condition to `while index < 4`, the code would
418panic. Using the `for` loop, you don’t need to remember to change any other
419code if you changed the number of values in the array.
420
421The safety and conciseness of `for` loops make them the most commonly used loop
422construct in Rust. Even in situations in which you want to run some code a
423certain number of times, as in the countdown example that used a `while` loop
abe05a73 424in Listing 3-3, most Rustaceans would use a `for` loop. The way to do that
cc61c64b
XL
425would be to use a `Range`, which is a type provided by the standard library
426that generates all numbers in sequence starting from one number and ending
427before another number.
428
429Here’s what the countdown would look like using a `for` loop and another method
430we’ve not yet talked about, `rev`, to reverse the range:
431
432<span class="filename">Filename: src/main.rs</span>
433
434```rust
435fn main() {
436 for number in (1..4).rev() {
437 println!("{}!", number);
438 }
439 println!("LIFTOFF!!!");
440}
441```
442
443This code is a bit nicer, isn’t it?
444
445## Summary
446
447You made it! That was a sizable chapter: you learned about variables, scalar
7cac9316 448and `if` expressions, and loops! If you want to practice with the concepts
cc61c64b
XL
449discussed 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,”
454taking advantage of the repetition in the song.
455
456When you’re ready to move on, we’ll talk about a concept in Rust that *doesn’t*
457commonly exist in other programming languages: ownership.