Skip to content

Commit fe72df2

Browse files
more lessons
1 parent 39974dc commit fe72df2

4 files changed

Lines changed: 1550 additions & 1 deletion

File tree

Lines changed: 318 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,321 @@
11
---
22
title: Control Flow & Functions
33
sidebar_position: 2
4-
---
4+
---
5+
6+
-----
7+
8+
# Lesson 2: Rust Fundamentals - Control Flow & Functions
9+
10+
We'll learn how to make decisions based on conditions, repeat actions, and organize our code into reusable blocks. By the end, we'll even build a game\!
11+
12+
-----
13+
14+
### 2.1 Conditional Execution: `if` Statements (15 min)
15+
16+
Just like in other programming languages, `if` statements in Rust let your program execute different code blocks based on whether a condition is true or false.
17+
18+
#### **Basic `if`, `else if`, `else`:**
19+
20+
You'll find this structure very familiar:
21+
22+
```rust
23+
fn main() {
24+
let number = 7;
25+
26+
if number < 5 { // If this condition is true
27+
println!("Condition was true: number is less than 5");
28+
} else if number == 5 { // Otherwise, if this condition is true
29+
println!("Condition was true: number is exactly 5");
30+
} else { // If none of the above conditions are true
31+
println!("Condition was false: number is greater than 5");
32+
}
33+
}
34+
```
35+
36+
* **Conditions Must Be `bool`:** In Rust, the condition inside an `if` statement *must* evaluate to a **boolean** (`true` or `false`). You can't just use a number like in some other languages.
37+
```rust
38+
// This would be an ERROR: `if number` is not allowed in Rust
39+
// if number {
40+
// println!("Number was something!");
41+
// }
42+
```
43+
44+
#### **`if` as an Expression:**
45+
46+
A cool feature in Rust is that `if` statements are **expressions**, meaning they can return a value. This is super handy for assigning values conditionally.
47+
48+
```rust
49+
fn main() {
50+
let condition = true;
51+
let number = if condition { // 'if' expression returns a value
52+
5 // This value is returned if 'condition' is true
53+
} else {
54+
6 // This value is returned if 'condition' is false
55+
}; // Note the semicolon here, as it's a statement assigning a value
56+
57+
println!("The value of number is: {}", number); // Output: The value of number is: 5
58+
59+
let message = if number > 5 {
60+
"Number is greater than 5"
61+
} else {
62+
"Number is 5 or less"
63+
}; // Both branches must return the SAME TYPE!
64+
65+
println!("Message: {}", message);
66+
}
67+
```
68+
69+
* **Important:** All branches of an `if` expression **must return the same type**. If one branch returns an integer and another returns a string, Rust won't compile because it can't determine the final type of the variable.
70+
71+
-----
72+
73+
### 2.2 Advanced Pattern Matching: The `match` Expression (10 min)
74+
75+
The `match` expression is one of Rust's most powerful control flow constructs. It allows you to compare a value against a series of patterns and then execute code based on which pattern matches. It's often a more robust and readable alternative to long `if-else if` chains.
76+
77+
* **Exhaustiveness:** A key feature of `match` is that it must be **exhaustive**. This means you have to cover *every possible value* that the data could take. If you don't, Rust's compiler will give you an error, which helps prevent bugs\!
78+
79+
Let's look at a simple example with numbers:
80+
81+
```rust
82+
fn main() {
83+
let number = 3;
84+
85+
match number { // Match the 'number' against these patterns
86+
1 => println!("One!"), // If number is 1, do this
87+
2 => println!("Two!"), // If number is 2, do this
88+
3 | 4 => println!("Three or Four!"), // If number is 3 OR 4, do this (multiple patterns)
89+
5..=10 => println!("Between 5 and 10, inclusive!"), // If number is in this range
90+
_ => println!("Something else!"), // The underscore '_' is a catch-all pattern (like 'default' in switch)
91+
}
92+
93+
let result = match number { // 'match' can also be an expression, returning a value
94+
1 => "It's one",
95+
_ => "It's not one", // All branches must return the same type!
96+
};
97+
println!("Result: {}", result);
98+
}
99+
```
100+
101+
* **When to use `match` vs. `if`:**
102+
* Use `if` for simple true/false conditions or a few distinct branches.
103+
* Use `match` when you have many possible values or complex patterns to handle, especially when working with `enum`s (which we'll cover in Lesson 3) or `Result` types (which you saw in Lesson 1's I/O).
104+
105+
-----
106+
107+
### 2.3 Iterative Control Structures (30 min)
108+
109+
Repeating actions is a fundamental part of programming. Rust provides several ways to create loops.
110+
111+
#### **`loop` (Infinite Loop with `break`):**
112+
113+
The `loop` keyword creates an infinite loop. You'll typically use `break` to exit it based on a condition, and `continue` to skip to the next iteration.
114+
115+
```rust
116+
fn main() {
117+
let mut counter = 0;
118+
119+
let result = loop { // 'loop' can also return a value!
120+
counter += 1;
121+
println!("Loop count: {}", counter);
122+
123+
if counter == 10 {
124+
break counter * 2; // Break the loop and return this value
125+
}
126+
}; // Semicolon here, as it's an expression
127+
128+
println!("Loop finished. Result: {}", result); // Output: Loop finished. Result: 20
129+
}
130+
```
131+
132+
#### **`while` Loop:**
133+
134+
A `while` loop executes a block of code repeatedly as long as a specified condition remains true.
135+
136+
```rust
137+
fn main() {
138+
let mut number = 3;
139+
140+
while number != 0 {
141+
println!("{}!", number);
142+
number -= 1; // Decrement number
143+
}
144+
println!("LIFTOFF!!!");
145+
}
146+
```
147+
148+
#### **`for` Loop (Iterating over Collections):**
149+
150+
The `for` loop is the most common loop in Rust. It's used to iterate over elements in a collection (like arrays, vectors, or ranges). This is often safer and more concise than `while` loops for iterating.
151+
152+
* **Iterating over a Range:**
153+
154+
```rust
155+
fn main() {
156+
// Iterate from 1 up to (but not including) 5
157+
for number in 1..5 {
158+
println!("Number in range: {}", number);
159+
}
160+
// Iterate from 1 up to AND including 5
161+
for number in 1..=5 {
162+
println!("Number in range (inclusive): {}", number);
163+
}
164+
}
165+
```
166+
167+
* **Iterating over an Array/Vector:**
168+
169+
```rust
170+
fn main() {
171+
let a = [10, 20, 30, 40, 50];
172+
173+
for element in a.iter() { // .iter() creates an iterator over the elements
174+
println!("The value is: {}", element);
175+
}
176+
177+
// You can also iterate with an index if needed (less common in idiomatic Rust)
178+
for (index, element) in a.iter().enumerate() {
179+
println!("Element at index {}: {}", index, element);
180+
}
181+
}
182+
```
183+
184+
-----
185+
186+
### 2.4 Defining and Utilizing Functions (15 min)
187+
188+
Functions are blocks of code that perform a specific task and can be reused.
189+
190+
#### **Basic Function Syntax:**
191+
192+
* Functions are declared using the `fn` keyword.
193+
* Parameters are type-annotated.
194+
* The return type is specified after an arrow `->`.
195+
* The last expression in a function (without a semicolon) is implicitly returned. You can also use the `return` keyword explicitly.
196+
197+
<!-- end list -->
198+
199+
```rust
200+
// A function that doesn't take parameters and doesn't return a value
201+
fn greet() {
202+
println!("Hello from the greet function!");
203+
}
204+
205+
// A function that takes parameters and returns a value
206+
fn add_numbers(x: i32, y: i32) -> i32 { // Takes two i32s, returns an i32
207+
x + y // This is an expression, implicitly returned
208+
}
209+
210+
// A function with an explicit return
211+
fn subtract_numbers(a: i32, b: i32) -> i32 {
212+
return a - b; // Explicit return
213+
}
214+
215+
fn main() {
216+
greet(); // Call the greet function
217+
218+
let sum = add_numbers(5, 7); // Call add_numbers and store the result
219+
println!("The sum is: {}", sum); // Output: The sum is: 12
220+
221+
let difference = subtract_numbers(10, 3);
222+
println!("The difference is: {}", difference); // Output: The difference is: 7
223+
}
224+
```
225+
226+
-----
227+
228+
### 2.5 Practical Application: Developing a Console-Based Guessing Game (20 min)
229+
230+
Let's put everything we've learned so far into practice by building a simple "Guess the Number" game in the console\!
231+
232+
**Game Logic:**
233+
234+
1. Generate a random secret number.
235+
2. Prompt the user to guess.
236+
3. Read the user's input.
237+
4. Compare the guess to the secret number.
238+
5. Tell the user if they guessed too high, too low, or correctly.
239+
6. Keep looping until the user guesses correctly.
240+
241+
**New Concepts/Tools:**
242+
243+
* **`rand` crate:** We'll need a library to generate random numbers. Add `rand = "0.8.5"` (or a recent version) to your `Cargo.toml` under `[dependencies]`.
244+
* **`use rand::Rng;`:** To bring the random number generator trait into scope.
245+
* **`parse()` method:** To convert the user's input string to a number. This also returns a `Result`, so we'll handle it.
246+
247+
**Steps to Build:**
248+
249+
1. **Create a new Cargo project:** `cargo new guessing_game`
250+
2. **Add `rand` dependency:** Open `Cargo.toml` and add `rand = "0.8.5"` under `[dependencies]`.
251+
3. **Open `src/main.rs`** and replace its content with the following:
252+
253+
<!-- end list -->
254+
255+
```rust
256+
// src/main.rs
257+
use std::io; // For input/output operations
258+
use rand::Rng; // For generating random numbers
259+
use std::cmp::Ordering; // For comparing numbers (used with match)
260+
261+
fn main() {
262+
println!("Guess the number!");
263+
264+
// Generate a random number between 1 and 100 (inclusive)
265+
// thread_rng() gives us a random number generator local to the current thread.
266+
// gen_range(1..=100) generates a number in the specified range.
267+
let secret_number = rand::thread_rng().gen_range(1..=100);
268+
269+
// For debugging, you can uncomment this line:
270+
// println!("The secret number is: {}", secret_number);
271+
272+
loop { // Start an infinite loop for the game
273+
println!("Please input your guess:");
274+
275+
let mut guess = String::new(); // Create a mutable string to store user input
276+
277+
// Read the user's guess from the console
278+
io::stdin()
279+
.read_line(&mut guess)
280+
.expect("Failed to read line"); // Handle potential errors
281+
282+
// Convert the guess from String to a number (u32).
283+
// .trim() removes any whitespace (like the newline character)
284+
// .parse() attempts to convert the string to a number. It returns a Result.
285+
// We use a 'match' expression to handle the Result:
286+
// - If Ok, we get the number.
287+
// - If Err, it means the input wasn't a valid number, so we print an error
288+
// and 'continue' to the next loop iteration (ask for guess again).
289+
let guess: u32 = match guess.trim().parse() {
290+
Ok(num) => num, // If parsing was successful, use the number
291+
Err(_) => { // If parsing failed (e.g., user typed text)
292+
println!("Please type a number!");
293+
continue; // Skip to the next iteration of the loop
294+
}
295+
};
296+
297+
println!("You guessed: {}", guess);
298+
299+
// Compare the guess to the secret number using 'match' and 'Ordering' enum
300+
// Ordering is an enum with variants Less, Greater, and Equal.
301+
match guess.cmp(&secret_number) {
302+
Ordering::Less => println!("Too small!"), // If guess is less than secret
303+
Ordering::Greater => println!("Too big!"), // If guess is greater than secret
304+
Ordering::Equal => { // If guess is equal to secret
305+
println!("You win!");
306+
break; // Exit the loop (and the game)
307+
}
308+
}
309+
}
310+
}
311+
```
312+
313+
**Run your game:**
314+
In your terminal, navigate into the `guessing_game` folder and run:
315+
`cargo run`
316+
317+
Now, play the game\! Try typing text instead of numbers to see the error handling.
318+
319+
-----
320+
321+
**End of Lesson 2.** You've now mastered Rust's core control flow, functions, and even built a complete interactive game\! This is a huge step in your Rust journey. Next, we'll tackle Rust's most unique and powerful concept: Ownership.

0 commit comments

Comments
 (0)