Skip to content

Commit c951f78

Browse files
added: new exercise, lessons 07,08,09,10,11,12
1 parent 1f0f787 commit c951f78

8 files changed

Lines changed: 754 additions & 0 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
---
2+
title: Pattern Matching
3+
---
4+
5+
---
6+
7+
### Advanced Pattern Matching: The `match` Expression (10 min)
8+
9+
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.
10+
11+
- **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\!
12+
13+
Let's look at a simple example with numbers:
14+
15+
```rust
16+
fn main() {
17+
let number = 3;
18+
19+
match number { // Match the 'number' against these patterns
20+
1 => println!("One!"), // If number is 1, do this
21+
2 => println!("Two!"), // If number is 2, do this
22+
3 | 4 => println!("Three or Four!"), // If number is 3 OR 4, do this (multiple patterns)
23+
5..=10 => println!("Between 5 and 10, inclusive!"), // If number is in this range
24+
_ => println!("Something else!"), // The underscore '_' is a catch-all pattern (like 'default' in switch)
25+
}
26+
27+
let result = match number { // 'match' can also be an expression, returning a value
28+
1 => "It's one",
29+
_ => "It's not one", // All branches must return the same type!
30+
};
31+
println!("Result: {}", result);
32+
}
33+
```
34+
35+
- **When to use `match` vs. `if`:**
36+
- Use `if` for simple true/false conditions or a few distinct branches.
37+
- 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).
38+
39+
---
Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
---
2+
title: Structs and Enums
3+
---
4+
5+
### Structuring Data with `struct`s
6+
7+
`struct`s (short for "structures") allow you to create custom data types by grouping related data together. They are similar to classes in object-oriented languages or objects/dictionaries in JavaScript/Python, but without built-in methods initially.
8+
9+
#### **Defining a `struct`:**
10+
11+
You define a `struct` using the `struct` keyword, followed by its name (typically `PascalCase`), and then curly braces containing its fields (each with a name and a type).
12+
13+
```rust
14+
// Define a struct named 'User'
15+
struct User {
16+
active: bool,
17+
username: String,
18+
email: String,
19+
sign_in_count: u64,
20+
}
21+
22+
fn main() {
23+
// Creating an instance of a struct
24+
let user1 = User { // Order of fields doesn't matter
25+
active: true,
26+
username: String::from("alice123"),
27+
email: String::from("alice@example.com"),
28+
sign_in_count: 1,
29+
};
30+
31+
// Accessing values using dot notation
32+
println!("User 1 Name: {}", user1.username);
33+
println!("User 1 Email: {}", user1.email);
34+
35+
// To modify a field, the struct instance itself must be mutable
36+
let mut user2 = User {
37+
active: false,
38+
username: String::from("bob456"),
39+
email: String::from("bob@example.com"),
40+
sign_in_count: 5,
41+
};
42+
43+
user2.email = String::from("new_bob@example.com"); // This is allowed
44+
println!("User 2 New Email: {}", user2.email);
45+
46+
// You can also create new instances from existing ones using the struct update syntax
47+
let user3 = User {
48+
email: String::from("charlie@example.com"),
49+
username: String::from("charlie789"),
50+
..user1 // Fills remaining fields from user1 (active, sign_in_count)
51+
};
52+
println!("User 3 Name: {}, Active: {}", user3.username, user3.active);
53+
}
54+
```
55+
56+
#### **Tuple Structs:**
57+
58+
Tuple structs are like tuples but have a name. They are useful when you want to give a name to a tuple but don't need named fields.
59+
60+
```rust
61+
struct Color(i32, i32, i32); // RGB values
62+
struct Point(i32, i32, i32); // X, Y, Z coordinates
63+
64+
fn main() {
65+
let black = Color(0, 0, 0);
66+
let origin = Point(0, 0, 0);
67+
68+
println!("Black RGB: ({}, {}, {})", black.0, black.1, black.2);
69+
// Note: black.0 is the first element, black.1 the second, etc.
70+
}
71+
```
72+
73+
#### **Unit-Like Structs:**
74+
75+
These are useful when you need to implement a trait on some type but don't have any data that you want to store inside the type itself.
76+
77+
```rust
78+
struct AlwaysEqual; // No fields
79+
80+
fn main() {
81+
let subject = AlwaysEqual;
82+
// You can use it as a type, but it holds no data.
83+
}
84+
```
85+
86+
#### **Printing Structs with `Debug` Trait:**
87+
88+
By default, `println!` cannot directly print structs in a readable format. You need to derive the `Debug` trait for your struct using `#[derive(Debug)]`.
89+
90+
```rust
91+
#[derive(Debug)] // Add this line above your struct definition
92+
struct User {
93+
active: bool,
94+
username: String,
95+
email: String,
96+
sign_in_count: u64,
97+
}
98+
99+
fn main() {
100+
let user1 = User {
101+
active: true,
102+
username: String::from("alice123"),
103+
email: String::from("alice@example.com"),
104+
sign_in_count: 1,
105+
};
106+
107+
println!("User 1: {:?}", user1); // Use {:?} for debug printing
108+
println!("User 1 (pretty print): {:#?}", user1); // Use {:#?} for pretty printing
109+
}
110+
```
111+
112+
---
113+
114+
### Modeling Data with `enum`s
115+
116+
`enum`s (enumerations) allow you to define a type by enumerating its possible variants. In Rust, `enum`s are much more powerful than in many other languages; they are "sum types," meaning a value of an `enum` can be _one of_ a set of defined possibilities.
117+
118+
#### **Simple `enum`s:**
119+
120+
You've already seen `Ordering` in the guessing game, which is a simple enum.
121+
122+
```rust
123+
enum TrafficLight {
124+
Red,
125+
Yellow,
126+
Green,
127+
}
128+
129+
fn main() {
130+
let current_light = TrafficLight::Red;
131+
132+
match current_light { // Often used with 'match' for exhaustive handling
133+
TrafficLight::Red => println!("Stop!"),
134+
TrafficLight::Yellow => println!("Prepare to stop!"),
135+
TrafficLight::Green => println!("Go!"),
136+
}
137+
}
138+
```
139+
140+
#### **`enum`s with Associated Data:**
141+
142+
This is where Rust's enums become extremely powerful. Each variant of an enum can hold its own specific data.
143+
144+
```rust
145+
enum Message {
146+
Quit, // No data
147+
Move { x: i32, y: i32 }, // Anonymous struct-like data
148+
Write(String), // Single String data
149+
ChangeColor(i32, i32, i32), // Tuple-like data (RGB values)
150+
}
151+
152+
fn main() {
153+
let m1 = Message::Quit;
154+
let m2 = Message::Move { x: 10, y: 20 };
155+
let m3 = Message::Write(String::from("hello"));
156+
let m4 = Message::ChangeColor(255, 0, 128);
157+
158+
// Using match to destructure and handle different enum variants
159+
match m2 {
160+
Message::Quit => println!("The Quit message has no data."),
161+
Message::Move { x, y } => println!("Move to x: {}, y: {}", x, y),
162+
Message::Write(text) => println!("Write message: {}", text),
163+
Message::ChangeColor(r, g, b) => println!("Change color to R:{}, G:{}, B:{}", r, g, b),
164+
}
165+
}
166+
```
167+
168+
#### **The `Option<T>` Enum (Handling Absence of a Value):**
169+
170+
`Option<T>` is a standard library enum that represents a value that might or might not be present. It's Rust's way of handling null/nil without null pointer exceptions.
171+
172+
```rust
173+
enum Option<T> { // Conceptual definition
174+
None, // Represents no value
175+
Some(T), // Represents a value of type T
176+
}
177+
178+
fn main() {
179+
let some_number = Some(5); // A value is present
180+
let no_number: Option<i32> = None; // No value is present
181+
182+
// You MUST use match (or other Option methods) to safely get the value out
183+
match some_number {
184+
Some(value) => println!("We have a number: {}", value),
185+
None => println!("No number here."),
186+
}
187+
188+
match no_number {
189+
Some(value) => println!("We have a number: {}", value),
190+
None => println!("No number here."),
191+
}
192+
}
193+
```
194+
195+
#### **The `Result<T, E>` Enum (Handling Recoverable Errors):**
196+
197+
`Result<T, E>` is another fundamental enum for handling operations that can succeed or fail. You saw it with `read_line()` in Lesson 1.
198+
199+
```rust
200+
enum Result<T, E> { // Conceptual definition
201+
Ok(T), // Represents success, holding a value of type T
202+
Err(E), // Represents failure, holding an error of type E
203+
}
204+
205+
fn main() {
206+
// Example: A function that might fail
207+
fn divide(numerator: f64, denominator: f64) -> Result<f64, String> {
208+
if denominator == 0.0 {
209+
Err(String::from("Cannot divide by zero!"))
210+
} else {
211+
Ok(numerator / denominator)
212+
}
213+
}
214+
215+
let division_result = divide(10.0, 2.0);
216+
match division_result {
217+
Ok(value) => println!("Division successful: {}", value),
218+
Err(error) => println!("Division failed: {}", error),
219+
}
220+
221+
let division_by_zero = divide(10.0, 0.0);
222+
match division_by_zero {
223+
Ok(value) => println!("Division successful: {}", value),
224+
Err(error) => println!("Division failed: {}", error),
225+
}
226+
}
227+
```
228+
229+
- **Key takeaway for `Option` and `Result`:** Rust forces you to explicitly handle the possibility of a value being absent or an operation failing, leading to more robust code.
230+
231+
---

0 commit comments

Comments
 (0)