A beginner-friendly course for curious young minds
This course teaches you how to write safe and fast programs using the Rust programming language. You will learn step by step, with lots of examples, stories, and fun activities.
Your first steps into systems programming.
Control flow in Rust.
if, else if, and else&&, ||, and !matchloop, while, and forStructs, enums, and error handling.
implOptionResultunwrap, expect, and the ? operatorDebugStoring many values and writing flexible code.
| Module | Mini Project |
|---|---|
| Module One | My Daily Routine program |
| Module Two | Guess the Number game |
| Module Three | Student Report Card program |
| Module Four | Contact List or To-Do List app |
Start your journey today. Learn Rust, build safe programs, and become a systems programmer!
“Rust Programming for Systems” – A beginner’s course for curious minds
Hello, young coder! Have you ever wondered how computers talk to each other? How do they store your games, keep your messages safe, and run your favourite apps without crashing? The answer lies in systems programming. And the best language to learn systems programming today is Rust.
Rust is a language that helps you build fast, safe, and reliable programs. It is used by big companies like Microsoft, Google, and Amazon. But don’t worry—it is also fun and friendly for beginners. In this module, we will start from the very beginning. We will learn what Rust is, why it is special, and how to write your very first Rust programs. No prior coding experience needed. Just bring your curiosity!
We will use simple words, lots of stories, and examples from Nigeria and around the world. By the end, you will understand the heart of Rust and be ready to write your own small programs.
After finishing this module, you will be able to:
Once upon a time in Lagos, Nigeria, there lived a clever girl named Ada. Ada loved building things. One day, she built a small robot named Chidi. Chidi could walk, talk, and even fetch water. But there was a problem: Chidi kept crashing into walls! Every time Ada gave Chidi a command, Chidi would get confused and fall down.
Ada asked her friend, Tunde, who was a computer whiz. Tunde said, “Ada, the problem is not Chidi’s body. It is Chidi’s brain. The instructions inside Chidi are not safe. The computer language you used lets Chidi make mistakes that the computer cannot catch. You need a language that checks your work before the robot moves.”
Ada was curious. “Which language can do that?” she asked. Tunde smiled and said, “Rust! Rust is like a careful teacher. It checks every instruction before the robot runs. It stops mistakes that could make Chidi crash. With Rust, Chidi can walk safely and never bump into walls again.”
Ada decided to learn Rust. And that is exactly what we are going to do today. Just like Ada, we will learn how Rust helps us build safe and strong programs—without crashing!
Moral of the story: Rust helps you write code that is safe and correct, so your programs (or robots!) don’t break easily.
Definition: Systems programming is writing software that controls the computer’s hardware and makes other programs run. It is like being the engineer who builds the roads and bridges that other cars (apps) drive on.
Why it is important: Without systems programming, your computer would not know how to turn on, save files, or connect to the internet.
Simple explanation: Imagine your computer is a big city. Systems programs are the roads, traffic lights, and water pipes. They keep everything moving. Apps like games and browsers are the cars and houses that use those roads.
Real-life example: The operating system (like Windows, macOS, or Linux) is a systems program. It manages your keyboard, screen, and memory.
School example: The school timetable is like a systems program. It tells each class when to go to which room. Without it, chaos!
Home example: Your home’s electrical wiring is a system. It delivers power to your TV, fridge, and lights safely.
Nigerian example: The traffic lights in Abuja are systems. They control when cars move and stop. If they fail, there is a big hold-up. Systems programming keeps such systems working.
Illustration:
+-------------------+ +-------------------+
| HARDWARE | | SYSTEMS |
| (CPU, memory, |<----->| PROGRAM |
| disk, screen) | | (Operating System)|
+-------------------+ +-------------------+
^ ^
| |
| |
+-------------------+ +-------------------+
| APPLICATIONS | | RUST CODE |
| (Games, browsers) | | (You write this!) |
+-------------------+ +-------------------+
Mini summary: Systems programming builds the foundation that all other software uses. Rust is a language made for this job.
Definition: Rust is a programming language that focuses on speed, safety, and doing many things at once (concurrency).
Why it is important: Many programs crash because of memory errors. Rust stops those errors before the program even runs.
Simple explanation: Think of Rust as a strict but caring coach. It checks your code and says, “Wait, this part might cause a problem. Let’s fix it now.” Other languages might let you run and then crash later.
Real-life example: A bank uses Rust to handle millions of transactions. If the program crashes, people lose money. Rust prevents crashes.
School example: A teacher who marks your homework before you submit it is like Rust. A teacher who only marks after you fail is like some other languages.
Home example: Before you cook, your mum checks if the gas is off. Rust checks your code before it runs.
Nigerian example: In a busy market like Balogun in Lagos, a good trader counts money carefully before giving change. Rust counts and checks memory carefully to avoid “change” errors.
Illustration:
Other languages: Rust:
Write code Write code
| |
V V
Run program Compiler checks
| |
V V
Maybe crash! If safe, run
Else, fix first
Mini summary: Rust is safe, fast, and prevents many common bugs. That is why big companies love it.
Definition: Installing Rust means putting the Rust tools on your computer so you can write and run Rust programs.
Why it is important: You cannot build a house without tools. Rust has its own tools: rustc (the compiler) and cargo (the project manager).
Simple explanation: rustc turns your human-readable code into computer instructions. cargo helps you create projects, add libraries, and run tests.
Real-life example: A carpenter uses a hammer and saw. A Rust programmer uses rustc and cargo.
School example: Your school bag holds your books and pens. Cargo is like your school bag for Rust projects.
Home example: A toolbox in your house has a screwdriver and pliers. The Rust installation gives you a toolbox for coding.
Nigerian example: A tailor in Kano uses a sewing machine and scissors. A Rust programmer uses rustc and cargo to “stitch” code together.
Illustration:
Your Computer
|
V
Install Rust (from rustup.rs)
|
+--> rustc (compiler)
|
+--> cargo (project manager)
|
V
Ready to write Rust!
Mini summary: Install Rust using rustup. Then you get rustc and cargo to build programs.
Definition: A “Hello, World!” program is the simplest program that prints a message to the screen.
Why it is important: It confirms that your tools work and you can run Rust code.
Simple explanation: You write a few lines. The computer reads them and shows “Hello, World!” on your screen.
Real-life example: When you say “Hello” to a friend, they say “Hello” back. Here, you tell the computer to say “Hello, World!”.
School example: The first day of school, you introduce yourself. This is your program’s introduction.
Home example: When you knock on a door, someone answers. This program knocks on the computer’s door.
Nigerian example: In Nigeria, we greet with “How are you?” The computer greets with “Hello, World!”.
Illustration:
fn main() {
println!("Hello, World!");
}
Step-by-step explanation:
fn main() { ... } – This is the starting point. Every Rust program begins here.println!("Hello, World!"); – This prints the text inside the quotes.; tells Rust that the instruction is finished.Mini summary: “Hello, World!” is your first step. It shows you how to write and run a basic Rust program.
Definition: A variable is a named box that stores a value. You can change what is inside (unless it is immutable).
Why it is important: Programs need to remember numbers, words, and other data. Variables hold that data.
Simple explanation: Imagine a kitchen cupboard. You put rice in one container, beans in another. You label them “rice” and “beans”. In Rust, you write let rice = 5;.
Real-life example: Your school bag has pockets for books, pens, and lunch. Each pocket is a variable.
School example: A register has columns for names, ages, and scores. Each column is like a variable.
Home example: Your phone’s contact list stores names and numbers. Each contact is a variable.
Nigerian example: A trader in Onitsha market has baskets for tomatoes, peppers, and onions. Each basket is a variable.
Illustration:
let age = 13; let name = "Ada"; let is_hungry = true;
Mini summary: Variables store data. Use let to create them. You can change them later if you use mut.
Definition: A data type tells Rust what kind of value a variable holds: a number, a word, a true/false, etc.
Why it is important: Rust needs to know if you are adding numbers or joining words. Different types have different rules.
Simple explanation: You don’t put water in a paper bag. You use a bottle. Similarly, you use i32 for whole numbers, f64 for decimals, bool for true/false, and String for text.
Real-life example: A fuel gauge shows numbers. A name tag shows words. A light switch shows on/off (bool).
School example: Your report card has numbers for scores, letters for grades, and yes/no for attendance.
Home example: A TV remote has buttons for numbers (channels), on/off (bool), and volume (numbers).
Nigerian example: A market woman sells yams (counted in numbers), peppers (measured in cups), and “sold out” (true/false).
Table of common types:
| Type | What it stores | Example |
|---|---|---|
| i32 | Whole number (integer) | 42, -7, 0 |
| f64 | Decimal number | 3.14, 2.5 |
| bool | True or false | true, false |
| String | Text | "Hello", "Lagos" |
| char | Single character | 'A', '7' |
Mini summary: Data types keep your data organised. Rust checks that you use the right type.
Definition: Mutability means you can change the value of a variable. In Rust, variables are immutable (cannot change) by default. You must add mut to make them mutable.
Why it is important: Immutability prevents accidental changes. It makes programs safer and easier to understand.
Simple explanation: Imagine a pencil writing on paper. You can erase and rewrite (mutable). But a pen writes permanently (immutable). Rust starts with pen by default; you choose pencil with mut.
Real-life example: Your birthday date never changes (immutable). Your age changes every year (mutable).
School example: Your name is fixed (immutable). Your class teacher can change (mutable).
Home example: The number of chairs in your house can change when guests come (mutable). The address of your house is fixed (immutable).
Nigerian example: The price of fuel can change (mutable). The location of your house is fixed (immutable).
Illustration:
let x = 5; // immutable, cannot change // x = 10; // ERROR! let mut y = 5; // mutable y = 10; // OK!
Mini summary: Use let for unchangeable variables. Use let mut for changeable ones. Rust loves safety.
Definition: A function is a named block of code that does a specific job. You can call it many times.
Why it is important: Functions avoid repeating code. They make programs shorter and easier to fix.
Simple explanation: Think of a function as a recipe. You write it once, then use it whenever you want to cook that dish.
Real-life example: A tailor has a pattern for a shirt. He uses the same pattern for many customers (function calls).
School example: The school bell rings at the same time every day. It is like a function called “ringBell()”.
Home example: Your blender has a “smoothie” button. You press it, and it does the same steps each time.
Nigerian example: A taxi driver has a route from Ikeja to Yaba. He repeats the same route (function) for many passengers.
Illustration:
fn greet() {
println!("Hello, friend!");
}
fn main() {
greet(); // calls the function
greet(); // calls it again
}
Mini summary: Functions are named blocks. Use fn to define them and () to call them.
Definition: Comments are lines in your code that the computer ignores. They are for you and other humans to read.
Why it is important: Comments explain why you did something. They help you remember later and help others understand.
Simple explanation: Imagine writing notes in the margin of your school book. The teacher doesn’t mark them, but they help you study.
Real-life example: A recipe has notes like “bake for 20 minutes”. The oven ignores the note, but the cook reads it.
School example: Your class timetable might have small notes like “bring calculator”. They are comments for students.
Home example: A sticky note on the fridge says “buy milk”. It is a comment for the family.
Nigerian example: A signboard in traffic says “no parking”. It is a comment for drivers, not for the road.
Illustration:
// This is a single-line comment
let x = 5; // This comment explains the variable
/*
This is a multi-line comment.
It can span many lines.
*/
Mini summary: Use // for short comments and /* ... */ for longer ones. They make code friendly.
Definition: The compiler is a program that turns your Rust code into a program the computer can run. It also checks for mistakes.
Why it is important: The compiler catches many errors before you run the program. This saves time and prevents crashes.
Simple explanation: The compiler is like a teacher who checks your homework before you submit it. If there is a mistake, the teacher tells you to fix it.
Real-life example: A building inspector checks a house before people move in. The Rust compiler checks your code before it runs.
School example: Before a debate, your teacher checks your speech for errors. That is the compiler.
Home example: Before cooking, your mum checks if all ingredients are available. That is the compiler.
Nigerian example: Before a bus leaves the park, the conductor checks that all passengers have paid. The compiler checks your code.
Illustration:
Your code (hello.rs)
|
V
rustc hello.rs <-- compiler
|
V
If no errors: hello executable
If errors: messages to fix
Mini summary: The Rust compiler is strict but helpful. It catches mistakes early. Always fix compiler errors.
Definition: Cargo is Rust’s tool for creating projects, building code, running tests, and adding libraries.
Why it is important: Without Cargo, managing big projects is hard. Cargo makes everything organised.
Simple explanation: Cargo is like a project folder with extra powers. It creates a structure, downloads dependencies, and runs your program.
Real-life example: A wedding planner organises the venue, food, and music. Cargo organises your Rust project.
School example: A class monitor collects homework and keeps things in order. Cargo is the monitor for your code.
Home example: A toolbox keeps all tools in one place. Cargo is your coding toolbox.
Nigerian example: An event planner for a Nigerian wedding handles aso-ebi, food, and canopies. Cargo handles your project’s files and libraries.
Illustration:
cargo new my_project
|
V
my_project/
├── Cargo.toml
└── src/
└── main.rs
Mini summary: Use cargo new to start a project. Use cargo run to run it. Cargo keeps everything tidy.
Definition: Printing means showing text on the screen. Input means the computer reads what you type.
Why it is important: Programs need to communicate with people. Printing shows results. Input lets users give data.
Simple explanation: Printing is like speaking. Input is like listening. A good program does both.
Real-life example: An ATM prints your balance. It also takes your PIN as input.
School example: A teacher asks a question (input) and writes the answer on the board (print).
Home example: A TV remote takes input (button press) and prints the channel on screen.
Nigerian example: A POS machine takes your card details (input) and prints a receipt (output).
Illustration:
use std::io;
fn main() {
println!("What is your name?");
let mut name = String::new();
io::stdin().read_line(&mut name).expect("Failed");
println!("Hello, {}!", name.trim());
}
Mini summary: Use println! to print. Use stdin().read_line to read input. Always trim extra spaces.
Definition: Mistakes (bugs) are errors in code. Rust catches many before running.
Why it is important: Knowing common mistakes helps you avoid them.
Simple explanation: Rust is like a spell-checker. It underlines errors and suggests fixes.
Real-life example: Typing a wrong phone number. Rust checks if the number is valid.
School example: Forgetting to carry your pencil. Rust checks if you have all variables.
Home example: Forgetting to turn off the tap. Rust checks if memory is freed.
Nigerian example: Forgetting your change in a taxi. Rust checks that you don’t lose data.
Table of common mistakes:
| Mistake | What happens | Rust’s help |
|---|---|---|
Using a variable without mut | Cannot change value | Compiler error |
| Forgetting semicolon | Unexpected code | Compiler error |
| Wrong data type | Type mismatch | Compiler error |
| Reading input without trim | Extra newline | You must trim |
Mini summary: Rust catches many mistakes early. Read compiler messages and fix them.
Definition: Best practices are good habits that make your code clean and safe.
Why it is important: Good habits make coding easier and more fun.
Simple explanation: Like brushing your teeth daily, coding habits keep your programs healthy.
Real-life example: Keeping your room tidy. Best practices keep code tidy.
School example: Writing neatly in your notebook. Best practices make code readable.
Home example: Washing plates after eating. Best practices prevent problems later.
Nigerian example: Keeping your market stall organised. Best practices keep code organised.
List of best practices:
score not s).cargo to manage projects.Mini summary: Good habits make you a better programmer. Start them now.
| Word | Simple Definition |
|---|---|
| Systems programming | Writing software that controls hardware and supports other programs. |
| Rust | A safe, fast programming language for systems. |
| Compiler | A program that turns your code into a runnable program and checks for errors. |
| Cargo | Rust’s tool for managing projects. |
| Variable | A named box that stores a value. |
| Data type | The kind of value a variable holds (number, text, true/false). |
| Immutable | Cannot be changed. |
| Mutable | Can be changed. |
| Function | A reusable block of code that does a job. |
| Comment | A note in code that the computer ignores. |
| Show text on the screen. | |
| Input | Data typed by the user. |
| Bug | A mistake in code. |
| Concurrency | Doing many things at the same time. |
mut.rustup.rs.cargo new hello_rust to create a project.cd hello_rust.src/main.rs in a text editor.
fn main() {
println!("Hello, World!");
}
cargo run.Hello, World! on the screen.Explanation: cargo new creates the project structure. cargo run compiles and runs your code. The main function is the starting point. println! prints text.
cargo can run tests for your code automatically?let to create them.mut to make a variable changeable.mut.main() correctly.cargo check often to catch errors.cargo fmt.cargo test.
Your Code (main.rs)
|
V
Rust Compiler (rustc)
|
+--> Errors? --> Fix and recompile
|
V
Executable Program
|
V
Runs on your computer
my_project/
├── Cargo.toml (project settings)
└── src/
└── main.rs (your code)
Start
|
V
Need to change the value later?
|
+-- Yes --> Use let mut x = ...
|
+-- No --> Use let x = ...
|
V
End
| Feature | Rust | Python | C++ |
|---|---|---|---|
| Speed | Very fast | Slower | Very fast |
| Memory safety | Yes, compiler checks | Automatic, but slower | Manual, error-prone |
| Learning curve | Moderate | Easy | Hard |
| Concurrency | Safe and easy | Limited | Hard |
| Use case | Systems, embedded, web | Scripts, data science | Games, systems |
| Property | Immutable | Mutable |
|---|---|---|
| Can change value? | No | Yes |
| Keyword | let | let mut |
| Safety | Higher | Lower, but flexible |
| Example | let x = 5; | let mut y = 5; y = 10; |
Lesson 1: Systems programming controls hardware and supports apps. Rust is a great language for it.
Lesson 2: Rust is safe, fast, and concurrent. It prevents many crashes.
Lesson 3: Install Rust with rustup. You get rustc and cargo.
Lesson 4: “Hello, World!” prints a message. Every Rust program starts with fn main().
Lesson 5: Variables store data. Use let.
Lesson 6: Data types tell Rust what kind of value you have.
Lesson 7: Immutable by default. Use mut to change.
Lesson 8: Functions are reusable blocks of code.
Lesson 9: Comments are notes for humans.
Lesson 10: The compiler checks your code and finds errors.
Lesson 11: Cargo manages projects and libraries.
Lesson 12: Printing shows text. Input reads what you type.
Lesson 13: Common mistakes include missing semicolons and wrong types.
Lesson 14: Best practices make code clean and safe.
Congratulations! You have finished Module One. You learned that systems programming is the foundation of all software. Rust is a modern language that is safe, fast, and perfect for systems. You installed Rust, wrote your first program, and learned about variables, data types, mutability, functions, comments, the compiler, Cargo, and input/output. You also saw many examples from Nigeria and everyday life. You now know common mistakes and best practices. Most importantly, you are ready to write simple Rust programs and understand how they work. Keep practising, and you will become a great Rust programmer!
mut? To allow a variable to change. Rust defaults to safety.Match the word to its definition.
| Word | Definition |
|---|---|
| 1. Variable | A. A reusable block of code. |
| 2. Function | B. A named box that stores a value. |
| 3. Compiler | C. A note in code that the computer ignores. |
| 4. Comment | D. Turns code into a runnable program. |
| 5. Cargo | E. Rust’s project manager. |
Answers: 1-B, 2-A, 3-D, 4-C, 5-E
let mut score = 0;.std::io::stdin().read_line().let x = 5; x = 10; and get an error. Why? let mut x = 5;.Title: “Build a Greeting Robot”
Instructions: In groups of 3–4, write a Rust program that asks for each person’s name and then prints a greeting for each. Use a function for the greeting. One person types, one person reads the compiler errors, one person explains the code. Share your program with the class.
Goal: Practice variables, input, functions, and printing.
Task: Write a Rust program that stores your name, age, and favourite food in variables. Then print them in a sentence like: “My name is Ada, I am 13 years old, and I love jollof rice.”
Hint: Use let and println!.
Project: “My Daily Routine”
Write a Rust program that prints your daily routine. Use at least three functions: one for morning, one for afternoon, and one for evening. Call them in main(). Add comments to explain each part.
Example output:
Morning: Wake up, brush teeth, eat breakfast. Afternoon: Go to school, study, play. Evening: Do homework, eat dinner, sleep.
Assignment: Create a new Cargo project called about_me. In main.rs, write a program that:
Submit: Your main.rs file and a screenshot of the program running.
rustup and use cargo for projects.let and mut.println!, read input with stdin().read_line().In Module Two, we will dive deeper into Rust. We will learn about:
To prepare, make sure you have completed the practical assignment and can run simple Rust programs. Review your notes on variables and functions. Bring your curiosity!
See you in Module Two!
End of Module One – Rust Programming for Systems
“Rust Programming for Systems” – A beginner’s course for curious minds
Welcome back, young coder! In Module One, you learned what Rust is, how to install it, and how to write simple programs with variables and functions. You even made the computer talk with println!.
But a program that only runs straight from top to bottom is boring. Real programs need to make decisions and repeat things. Imagine a traffic light that never changes, or a game that never checks if you won. That would be useless!
In this module, we will learn two superpowers: decision making (using if, else, and match) and repeating actions (using loops). We will also peek at Rust’s most special idea: ownership. This is what makes Rust safe and fast. Don’t worry—we will use simple stories, examples from Nigeria, and lots of pictures made with text.
By the end, you will be able to write programs that think and act, just like a smart robot. Let’s begin!
After finishing this module, you will be able to:
if, else if, and else to make decisions in Rust.match to compare a value against many patterns.loop, while, and for to repeat actions.Remember Ada and her robot Chidi from Module One? After Ada learned Rust, she rebuilt Chidi’s brain. Now Chidi could walk without crashing. But Chidi still had a problem: Chidi did not know when to stop walking!
One day, Ada took Chidi to the market in Lagos. Chidi was supposed to follow Ada and stop when she stopped. But Chidi just kept walking. Ada shouted, “Chidi, stop!” But Chidi did not understand.
Ada realized that Chidi needed to make decisions. Chidi needed to check: “Is Ada in front of me? If yes, keep walking. If no, stop.” Chidi also needed to repeat this check many times, like a loop.
So Ada wrote a new program using if and while. She taught Chidi to check Ada’s position again and again. Now Chidi could decide when to walk and when to stop. Chidi became the smartest robot in Lagos!
Moral of the story: Programs need to make decisions and repeat actions to be useful. Rust gives us tools like if, match, and loops to do this.
ifDefinition: if is a keyword that runs a block of code only when a condition is true.
Why it is important: Programs need to choose different actions in different situations. if lets them do that.
Simple explanation: Imagine you are hungry. You check: “If I am hungry, I will eat.” The action (eating) only happens if the condition (hungry) is true.
Real-life example: A traffic light: if the light is red, cars stop. If green, cars go.
School example: If you score above 50, you pass the exam. If not, you fail.
Home example: If the pot is hot, use a cloth to hold it. If not, hold it directly.
Nigerian example: If it rains in Lagos, take an umbrella. If not, leave it at home.
Illustration:
let hungry = true;
if hungry {
println!("Time to eat!");
}
Step-by-step:
if followed by a condition.bool (true or false).{ and close with }.Mini summary: if runs code only when a condition is true. It is the first step to making your program smart.
else and else ifDefinition: else runs when the if condition is false. else if checks another condition if the first one is false.
Why it is important: Sometimes you need more than two choices. else if lets you check many conditions in order.
Simple explanation: If you are hungry, eat. Else if you are thirsty, drink. Else, rest.
Real-life example: A grading system: if score >= 70, grade A; else if score >= 60, grade B; else, grade C.
School example: If it is Monday, wear uniform. Else if it is Friday, wear sports wear. Else, wear casual.
Home example: If there is rice, cook rice. Else if there is beans, cook beans. Else, order food.
Nigerian example: If there is fuel, drive the car. Else if there is diesel, use the generator. Else, take a bus.
Illustration:
let score = 75;
if score >= 70 {
println!("Grade A");
} else if score >= 60 {
println!("Grade B");
} else {
println!("Grade C");
}
Mini summary: else if lets you check many conditions. else catches everything else. Use them together for smart decisions.
Definition: A condition is an expression that is either true or false. Boolean logic uses && (AND), || (OR), and ! (NOT).
Why it is important: You often need to check more than one thing at once.
Simple explanation: && means both must be true. || means at least one must be true. ! flips true to false and false to true.
Real-life example: You can ride a bike if you have a helmet AND you know how to ride.
School example: You can go to the library if you have a library card OR a teacher’s note.
Home example: You can watch TV if you have finished homework AND it is not too late.
Nigerian example: You can travel if you have your ticket AND your luggage is ready.
Illustration:
let has_helmet = true;
let knows_riding = false;
if has_helmet && knows_riding {
println!("You can ride!");
} else {
println!("Not yet safe to ride.");
}
if !knows_riding {
println!("You need to learn riding first.");
}
Mini summary: Use &&, ||, and ! to combine or flip conditions. They make your decisions more powerful.
match Expression – A Better Way to ChooseDefinition: match compares a value against many patterns and runs the code for the matching pattern.
Why it is important: It is cleaner and safer than many if else blocks. Rust checks that you cover all possibilities.
Simple explanation: Imagine a vending machine. You press a button (A1, B2, C3). The machine matches your button to a snack. match does the same with values.
Real-life example: A traffic light: match the colour to an action. Red -> stop, Yellow -> ready, Green -> go.
School example: Match the day of the week to the subject: Monday -> Maths, Tuesday -> English, etc.
Home example: Match the weather to what you wear: Sunny -> T-shirt, Rainy -> raincoat.
Nigerian example: Match the market day to the market you visit: Monday -> Oshodi, Wednesday -> Mile 12, etc.
Illustration:
let light = "red";
match light {
"red" => println!("Stop!"),
"yellow" => println!("Get ready..."),
"green" => println!("Go!"),
_ => println!("Unknown light"),
}
Step-by-step:
match followed by the value.{ }, write patterns with => and code._ as the “catch-all” pattern for anything else.Mini summary: match is a clean way to choose between many options. It forces you to think of all cases.
loopDefinition: loop repeats a block of code forever until you tell it to stop with break.
Why it is important: Some tasks need to repeat many times, like checking for input or counting.
Simple explanation: Imagine a fan that keeps spinning until you press the off button. loop is like that fan. break is the off button.
Real-life example: A washing machine repeats the wash cycle until the timer ends.
School example: The school bell rings every day at the same time. It is a loop.
Home example: Your alarm clock rings every morning until you turn it off.
Nigerian example: A generator keeps running until you switch it off. loop is the generator, break is the switch.
Illustration:
let mut count = 0;
loop {
println!("Count is {}", count);
count += 1;
if count == 5 {
break;
}
}
Mini summary: loop repeats forever. Use break to stop. Be careful not to create an infinite loop by mistake!
while Loops – Repeat While TrueDefinition: A while loop repeats as long as a condition is true.
Why it is important: It is perfect when you don’t know how many times to repeat, but you know when to stop.
Simple explanation: While you are hungry, keep eating. When you are full, stop.
Real-life example: While the traffic light is red, cars wait. When it turns green, they go.
School example: While the teacher is talking, students listen. When the bell rings, they go out.
Home example: While the water is boiling, watch the pot. When it boils, turn off the stove.
Nigerian example: While there is fuel in the generator, it runs. When the fuel finishes, it stops.
Illustration:
let mut number = 3;
while number != 0 {
println!("{}!", number);
number -= 1;
}
println!("Liftoff!");
Mini summary: while checks a condition before each repeat. When the condition becomes false, the loop stops.
for Loops – Repeat a Known Number of TimesDefinition: A for loop repeats once for each item in a collection (like a range of numbers).
Why it is important: It is the safest and most common loop in Rust. It avoids mistakes with counters.
Simple explanation: For each day of the week, go to school. For each item in your bag, take it out.
Real-life example: For each passenger on a bus, collect the fare.
School example: For each student in the class, mark attendance.
Home example: For each plate in the sink, wash it.
Nigerian example: For each customer in the market, greet them “Welcome o!”.
Illustration:
for number in 1..5 {
println!("Number is {}", number);
}
let names = ["Ada", "Tunde", "Chidi"];
for name in names.iter() {
println!("Hello, {}!", name);
}
Mini summary: for loops go through each item. They are clean and safe. Use them when you know the collection.
Definition: An expression produces a value. A statement performs an action but produces no value.
Why it is important: Rust is an expression-based language. Many things are expressions, like if and match.
Simple explanation: 5 + 3 is an expression (it gives 8). let x = 5; is a statement (it does something but gives nothing).
Real-life example: “What is 2+2?” is an expression (answer 4). “Close the door” is a statement (action, no value).
School example: “What is your name?” (expression, answer). “Raise your hand” (statement, action).
Home example: “How many eggs?” (expression). “Put the eggs in the basket” (statement).
Nigerian example: “How much is the yam?” (expression). “Buy the yam” (statement).
Illustration:
let x = 5; // statement
let y = { // block expression
let a = 3;
a + 1 // no semicolon, so it is the value
};
println!("y is {}", y); // prints 4
Mini summary: Expressions give values. Statements do actions. In Rust, if and match can be expressions if you use them that way.
if in a let – Expressions in ActionDefinition: You can assign the result of an if or match expression to a variable.
Why it is important: It makes code shorter and cleaner.
Simple explanation: Instead of writing a long if else to set a variable, you can do it in one line.
Real-life example: “If it is sunny, wear sunglasses; else, wear a raincoat.” Assign that choice to “what to wear”.
School example: “If score is above 50, result is Pass; else, Fail.” Assign to “result”.
Home example: “If there is milk, breakfast is cereal; else, breakfast is bread.” Assign to “breakfast”.
Nigerian example: “If there is petrol, drive the car; else, take a bus.” Assign to “transport”.
Illustration:
let score = 75;
let result = if score >= 50 { "Pass" } else { "Fail" };
println!("Result: {}", result);
Mini summary: if and match can give values. Assign them to variables for cleaner code. Remember: no semicolon inside the block if you want the value.
Definition: Ownership is Rust’s system for managing memory. Each value has a single owner. When the owner goes out of scope, the value is dropped (freed).
Why it is important: It prevents memory bugs and makes Rust safe without a garbage collector.
Simple explanation: Imagine you have a toy. Only one person can own it at a time. If you give it to a friend, you no longer own it. Rust does the same with data.
Real-life example: A book in the library. Only one person can borrow it at a time. When returned, someone else can borrow it.
School example: A class monitor holds the attendance book. If they give it to another student, they no longer have it.
Home example: The TV remote. If you pass it to your sister, you cannot change the channel anymore.
Nigerian example: A single mobile phone. If you give it to your friend to make a call, you cannot use it until they return it.
Illustration:
let s1 = String::from("hello");
let s2 = s1; // s1 is moved to s2. s1 is no longer valid.
// println!("{}", s1); // ERROR: s1 no longer owns the data
println!("{}", s2); // OK
Mini summary: Ownership means one owner at a time. When you assign a String to another variable, the original is moved. This prevents double-free errors.
Definition: Borrowing lets you use a value without taking ownership. You use & to create a reference.
Why it is important: It lets functions use data without stealing it from the owner.
Simple explanation: You can let a friend look at your book without giving it away. They borrow it and give it back.
Real-life example: A library book you read in the library. You don’t own it, but you can read it.
School example: You borrow a pencil from a friend. You use it and return it.
Home example: You borrow your brother’s charger. You use it and give it back.
Nigerian example: You borrow a pot from your neighbour to cook. You return it after.
Illustration:
fn main() {
let s = String::from("hello");
let len = calculate_length(&s); // borrow s
println!("The length of '{}' is {}.", s, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
Mini summary: Borrowing uses & to reference a value without moving it. The owner keeps ownership. This is safe and efficient.
Definition: You can borrow a value mutably with &mut if you want to change it.
Why it is important: Sometimes you need to modify data without taking ownership.
Simple explanation: You borrow a friend’s notebook and write in it with their permission.
Real-life example: A teacher borrows your homework to correct it. They can write on it (with permission).
School example: You borrow the class register to add your name. You need permission.
Home example: You borrow your mum’s phone to add a contact. She must allow it.
Nigerian example: You borrow a trader’s ledger to record a sale. The trader must agree.
Illustration:
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s); // prints "hello, world"
}
fn change(s: &mut String) {
s.push_str(", world");
}
Mini summary: Use &mut to borrow and change. You can only have one mutable borrow at a time. This prevents data races.
Definition: Mistakes (bugs) happen. Knowing them helps you avoid them.
Why it is important: Rust’s compiler catches many mistakes. Learn from the messages.
Simple explanation: Like learning to ride a bike, you may wobble. Rust helps you stay balanced.
Real-life example: Forgetting to turn off the tap. Rust checks that you don’t leave memory leaks.
School example: Forgetting to carry your homework. Rust checks that you don’t forget variables.
Home example: Forgetting to lock the door. Rust checks that your data is safe.
Nigerian example: Forgetting your change in a taxi. Rust checks that you don’t lose data.
Table of common mistakes:
| Mistake | What happens | Rust’s help |
|---|---|---|
| Using a moved value | Value no longer valid | Compiler error: “value borrowed after move” |
| Multiple mutable borrows | Data race possible | Compiler error: “cannot borrow as mutable more than once” |
Missing _ in match | Not all cases covered | Compiler error: “non-exhaustive patterns” |
Infinite loop without break | Program never stops | You must add a break condition |
Using == with different types | Type mismatch | Compiler error |
Mini summary: Rust catches many mistakes early. Read the error messages. They tell you exactly what to fix.
Definition: Best practices are good habits that make your code clean and safe.
Why it is important: Good habits make coding easier and more fun.
Simple explanation: Like keeping your room tidy, best practices keep your code tidy.
Real-life example: A chef keeps the kitchen clean. A programmer keeps code clean.
School example: Writing neatly in your notebook. Best practices make code readable.
Home example: Washing plates after eating. Best practices prevent problems later.
Nigerian example: Keeping your market stall organised. Best practices keep code organised.
List of best practices:
for loops instead of while when possible. They are safer.match instead of long if else chains.& instead of moving when you don’t need ownership.cargo check often to catch errors.Mini summary: Good habits make you a better programmer. Start them now.
Let’s write a program that uses everything we learned: if, match, loops, and borrowing.
Program: Guess the Number (simplified)
use std::io;
fn main() {
let secret = 7;
let mut attempts = 0;
loop {
println!("Guess a number between 1 and 10:");
let mut guess = String::new();
io::stdin().read_line(&mut guess).expect("Failed to read");
let guess: i32 = guess.trim().parse().expect("Not a number");
attempts += 1;
if guess == secret {
println!("You win! You took {} attempts.", attempts);
break;
} else if guess < secret {
println!("Too low!");
} else {
println!("Too high!");
}
}
}
Explanation:
loop repeats until the correct guess.if else if else checks the guess.break exits the loop when correct.String::new() creates a new string.parse() converts text to a number.trim() removes extra spaces.Mini summary: This program combines decisions, loops, and input. You can build it and play!
| Word | Simple Definition |
|---|---|
| Control flow | The order in which code runs. Decisions and loops change it. |
if | Runs code when a condition is true. |
else | Runs code when the if condition is false. |
else if | Checks another condition if the first is false. |
match | Compares a value to many patterns. |
loop | Repeats forever until break. |
while | Repeats while a condition is true. |
for | Repeats for each item in a collection. |
| Expression | Produces a value. |
| Statement | Performs an action, no value. |
| Ownership | Each value has one owner. When owner goes away, value is dropped. |
| Borrowing | Using a value without taking ownership (&). |
| Mutable borrow | Borrowing to change (&mut). |
| Move | Transferring ownership from one variable to another. |
| Scope | The part of code where a variable is valid. |
if and else use bool values.match is powerful and safe.loop, while, and for repeat actions.if and match can give values.& to reference without moving. Use &mut to change.match step by stepmatch value {.=> and the code to run._ if needed.}.Example:
let day = "Monday";
match day {
"Monday" => println!("Start of week"),
"Friday" => println!("Almost weekend"),
_ => println!("Just another day"),
}
fn my_func(s: &String).&my_variable.Example:
fn main() {
let s = String::from("hello");
print_length(&s);
println!("Still own: {}", s);
}
fn print_length(s: &String) {
println!("Length: {}", s.len());
}
match to decide stop, ready, or go.if to check if PIN is correct.loop to keep the game running until you quit.for to play each song in a playlist.while to keep asking until valid input.if to check if card is valid.match to decide when to stop and go.while to keep selling while there is stock.for to play each episode.loop to keep running until fuel finishes.if checks if you won or lost.while keeps the robot moving while battery is full.loop keeps the app running until you close it.for plays each song in your playlist.match checks your answer against the correct one.while time is not up, keep ringing.for each second, count down.match colour to action.if PIN is correct, allow withdrawal.for each tap, show letter.match keyword is one of Rust’s most loved features.for loop is safer than C’s for loop.match must cover every possible case?loop can return a value with break?if, else if, and else make decisions.match compares against many patterns.loop repeats forever; use break to stop.while repeats while a condition is true.for repeats for each item in a collection.& to reference, &mut to change.break in a loop._ in match.= instead of == in conditions.if conditions must be bool.for loops over while when possible.match instead of long if else chains.cargo check often.cargo fmt.cargo test.
Start
|
V
Is condition true? --Yes--> Run if block
|
No
|
V
Is else if condition true? --Yes--> Run else if block
|
No
|
V
Run else block
|
V
End
Start
|
V
Check condition (while) or enter loop
|
+--> Condition false? --> Exit loop
|
+--> Condition true? --> Run loop body
|
V
Go back to check
let s = String::from("hello"); // s owns the String
|
V
let r = &s; // r borrows s
|
V
println!("{}", r); // OK
println!("{}", s); // OK, s still owns
let s1 = String::from("hello");
|
V
let s2 = s1; // ownership moves to s2
|
V
s1 is no longer valid
|
V
s2 is the new owner
| Loop Type | When to Use | Example |
|---|---|---|
loop | Repeat forever until break | Game loop |
while | Repeat while condition is true | Wait for input |
for | Repeat for each item | Go through a list |
| Feature | Ownership | Borrowing |
|---|---|---|
| Who has the value? | One owner | Owner keeps it |
| Can you change? | Yes, if mutable | Only with &mut |
| Cost | Move transfers ownership | No move, just reference |
| Safety | Prevents double-free | Prevents data races |
| Expression | Statement |
|---|---|
| Produces a value | Performs an action |
5 + 3 | let x = 5; |
if true { 1 } else { 2 } | println!("hi"); |
Lesson 1: if runs code when a condition is true.
Lesson 2: else if and else add more choices.
Lesson 3: Use &&, ||, and ! to combine conditions.
Lesson 4: match compares a value to many patterns.
Lesson 5: loop repeats forever until break.
Lesson 6: while repeats while a condition is true.
Lesson 7: for repeats for each item in a collection.
Lesson 8: Expressions give values; statements do actions.
Lesson 9: if and match can be assigned to variables.
Lesson 10: Ownership: one owner at a time. Moving transfers ownership.
Lesson 11: Borrowing uses & to reference without moving.
Lesson 12: Mutable borrowing uses &mut to change borrowed data.
Lesson 13: Common mistakes include moved values and missing _ in match.
Lesson 14: Best practices: use for, match, and borrow when possible.
Lesson 15: Combine decisions, loops, and borrowing in a guessing game.
Congratulations! You have finished Module Two. You learned how to make programs think with if, else, and match. You learned how to repeat actions with loop, while, and for. You also learned Rust’s superpower: ownership and borrowing. These ideas make Rust safe and fast. You saw many examples from Nigeria and everyday life. You now know common mistakes and best practices. Most importantly, you can write programs that make decisions and repeat tasks. Keep practising, and you will become a great Rust programmer!
if and match? if checks one condition. match checks many patterns.loop vs while? Use loop when you want to repeat forever until a break. Use while when you have a condition.&.&mut.break.Match the word to its definition.
| Word | Definition |
|---|---|
1. if | A. Repeats for each item in a collection. |
2. match | B. Runs code when a condition is true. |
3. while | C. Compares a value to many patterns. |
4. for | D. Repeats while a condition is true. |
| 5. Ownership | E. Each value has one owner. |
Answers: 1-B, 2-C, 3-D, 4-A, 5-E
if else.match.loop or while.for.&.Title: “Build a Traffic Light Simulator”
Instructions: In groups of 3–4, write a Rust program that simulates a traffic light. Use match to choose the action for “red”, “yellow”, and “green”. Use a loop to repeat the cycle. Print the light colour and the action. Share your program with the class.
Goal: Practice match, loops, and printing.
Task: Write a Rust program that asks the user for a number. Use if else to print whether the number is positive, negative, or zero. Use a loop to let the user try again until they type “quit”.
Hint: Use parse() to convert input to a number. Use break to exit.
Project: “My School Timetable”
Write a Rust program that stores your school timetable for the week. Use match to print the subject for each day. Use a for loop to go through the days. Add comments to explain each part.
Example output:
Monday: Maths Tuesday: English Wednesday: Science Thursday: History Friday: Sports
Assignment: Create a new Cargo project called number_guess. In main.rs, write a program that:
loop to ask the user to guess.if else if else to say “Too low!”, “Too high!”, or “You win!”.Submit: Your main.rs file and a screenshot of the program running.
if, else if, and else for decisions.match for many patterns.loop, while, and for for repetition.& to reference, &mut to change.match different from if else?loop has no break?for often safer than while?In Module Three, we will dive deeper into Rust. We will learn about:
Result and Option.To prepare, make sure you have completed the practical assignment and can write programs with decisions and loops. Review your notes on ownership and borrowing. Bring your curiosity!
See you in Module Three!
End of Module Two – Rust Programming for Systems
“Rust Programming for Systems” – A beginner’s course for curious minds
Welcome back, young coder! In Module One, you learned how to store data in variables. In Module Two, you learned how to make decisions and repeat actions. Now it is time to build your own data types.
Imagine you are building a school management program. You need to store information about students: their names, ages, classes, and scores. If you use separate variables for each piece of information, your code will become a big, messy pile. Rust gives you a better way: structs and enums.
A struct groups related data together. An enum lets you choose one value from a list of possibilities. Together, they help you build clear, safe programs. We will also learn about error handling with Result and Option. These are Rust’s tools for handling things that might go wrong, without crashing.
By the end of this module, you will be able to design your own data types and write programs that handle errors gracefully. Let’s get started!
After finishing this module, you will be able to:
impl blocks.match with enums for pattern matching.Option and Result for error handling.unwrap, expect, and ? operator safely.Tunde is a bright boy in Abuja. His teacher asked him to keep a register of all the students in his class. At first, Tunde used a separate notebook for each student. He wrote name in one book, age in another, and score in another. Soon, the books were everywhere. He mixed up names and scores. It was a mess!
Tunde’s friend, Ada, said, “Tunde, why don’t you use one card for each student? Write the name, age, and score on the same card. Then you can keep all cards in one box.”
Tunde tried it. It was much better! Each card held all the information about one student. He could look at a card and see everything at once. He could also write notes on the card, like “Good in Maths” or “Needs help in English.”
In Rust, that card is called a struct. It groups related data together. And the notes are like methods—actions you can do with the card. Tunde’s new register was clean, safe, and easy to use. Just like Rust!
Moral of the story: Grouping related data together makes programs clean and safe. Structs help us do that.
Definition: A struct is a custom data type that groups related values together. Each value is called a field.
Why it is important: It keeps related data together, making code clean and easy to understand.
Simple explanation: Think of a student ID card. It has a name, age, class, and photo. All that information belongs to one student. A struct is like that card.
Real-life example: A bank account has a name, account number, and balance. All in one place.
School example: A student record with name, age, class, and score.
Home example: A recipe card with ingredients, steps, and cooking time.
Nigerian example: A market stall record with trader name, goods, price, and quantity.
Illustration:
struct Student {
name: String,
age: u32,
score: f64,
}
Step-by-step:
struct followed by the name (use PascalCase: Student).{ }.name: Type.Mini summary: A struct groups related data. It is like a card that holds many pieces of information about one thing.
Definition: An instance is a specific value created from a struct. You fill in the fields with actual data.
Why it is important: You need to create instances to use your structs in programs.
Simple explanation: The struct is the blank card. The instance is the card filled with a student’s details.
Real-life example: A blank form becomes an instance when you fill it with your name and address.
School example: A blank report card becomes an instance when it has your scores.
Home example: A shopping list becomes an instance when you write specific items.
Nigerian example: A blank POS receipt becomes an instance when it shows a transaction.
Illustration:
let student1 = Student {
name: String::from("Ada"),
age: 13,
score: 85.5,
};
println!("Name: {}", student1.name);
println!("Age: {}", student1.age);
println!("Score: {}", student1.score);
Mini summary: Create an instance by filling in the fields. Use dot notation to access fields.
Definition: A method is a function that belongs to a struct. It is defined inside an impl block.
Why it is important: Methods let you do things with your data in a clean way.
Simple explanation: If the struct is a card, methods are actions you can do with the card, like “show details” or “update score.”
Real-life example: A phone has methods: call, send message, take photo.
School example: A student card has a method: “calculate grade.”
Home example: A TV remote has methods: turn on, change channel, volume up.
Nigerian example: A POS machine has methods: accept payment, print receipt.
Illustration:
impl Student {
fn show_details(&self) {
println!("Name: {}, Age: {}, Score: {}", self.name, self.age, self.score);
}
fn is_pass(&self) -> bool {
self.score >= 50.0
}
}
Step-by-step:
impl StructName { }.fn method_name(&self) { }.&self means the method borrows the instance.instance.method_name().Mini summary: Methods are functions inside impl. They make structs powerful and clean.
Definition: An enum (enumeration) is a type that can be one of several variants.
Why it is important: It represents choices clearly and safely.
Simple explanation: Think of a traffic light. It can be Red, Yellow, or Green. Those are the only choices. An enum lists all possible choices.
Real-life example: Days of the week: Monday, Tuesday, Wednesday, etc.
School example: Grades: A, B, C, D, F.
Home example: Light switch: On or Off.
Nigerian example: Payment methods: Cash, Transfer, Card, USSD.
Illustration:
enum TrafficLight {
Red,
Yellow,
Green,
}
enum PaymentMethod {
Cash,
Transfer,
Card,
USSD,
}
Mini summary: Enums list possible choices. They make your code safe because you can only use the listed variants.
Definition: Enum variants can hold data. This makes enums very powerful.
Why it is important: Sometimes a choice comes with extra information.
Simple explanation: A payment can be Cash (with amount) or Card (with card number). The enum variant holds the data.
Real-life example: A message can be Text (with words), Image (with pixels), or Video (with frames).
School example: A result can be Pass (with score) or Fail (with reason).
Home example: A delivery can be Delivered (with time) or Pending (with expected date).
Nigerian example: A transfer can be Successful (with reference) or Failed (with error).
Illustration:
enum Payment {
Cash(f64),
Transfer { bank: String, amount: f64 },
Card(String),
}
Mini summary: Enum variants can carry data. This lets you attach extra information to each choice.
Definition: match works with enums to handle each variant.
Why it is important: It ensures you handle all possible variants safely.
Simple explanation: If the light is Red, stop. If Yellow, ready. If Green, go. match checks each variant.
Real-life example: A restaurant order: match the order to the kitchen action.
School example: Match the grade to a message: A -> Excellent, B -> Good, etc.
Home example: Match the weather to what to wear.
Nigerian example: Match the market day to the market location.
Illustration:
let light = TrafficLight::Red;
match light {
TrafficLight::Red => println!("Stop!"),
TrafficLight::Yellow => println!("Get ready..."),
TrafficLight::Green => println!("Go!"),
}
Mini summary: match with enums is safe and clean. Rust forces you to handle every variant.
Option – Handling Missing ValuesDefinition: Option is an enum that can be Some(value) or None.
Why it is important: It handles cases where a value might be missing, without using null.
Simple explanation: Imagine a box that may or may not have a gift. Some(gift) means there is a gift. None means empty.
Real-life example: A phone contact that may or may not have an email address.
School example: A student may or may not have a middle name.
Home example: A fridge may or may not have milk.
Nigerian example: A market stall may or may not have tomatoes today.
Illustration:
let some_number = Some(5); let no_number: Option= None; match some_number { Some(n) => println!("Number is {}", n), None => println!("No number"), }
Mini summary: Option handles missing values. Use Some and None. Always match both.
Result – Handling ErrorsDefinition: Result is an enum that can be Ok(value) or Err(error).
Why it is important: It handles operations that might fail, like reading a file or parsing a number.
Simple explanation: Imagine you are baking. The cake might come out well (Ok) or burn (Err). Result tells you which happened.
Real-life example: An ATM withdrawal: Ok(cash) or Err("Insufficient funds").
School example: An exam: Ok(score) or Err("Absent").
Home example: A phone call: Ok("Connected") or Err("No network").
Nigerian example: A transfer: Ok("Successful") or Err("Network failure").
Illustration:
fn divide(a: f64, b: f64) -> Result{ if b == 0.0 { Err(String::from("Cannot divide by zero")) } else { Ok(a / b) } } match divide(10.0, 2.0) { Ok(result) => println!("Result: {}", result), Err(e) => println!("Error: {}", e), }
Mini summary: Result handles success and failure. Use Ok for success and Err for errors. Always handle both.
unwrap and expect – Quick but RiskyDefinition: unwrap() and expect() extract the value from Option or Result. If there is no value, the program panics (crashes).
Why it is important: They are quick for testing, but risky for real programs.
Simple explanation: Imagine opening a box. If there is a gift, you take it. If empty, you scream and fall down. That is unwrap.
Real-life example: Pressing an elevator button. If it works, fine. If not, you are stuck.
School example: Assuming you will pass without studying. If you fail, you panic.
Home example: Assuming there is fuel in the generator. If not, it stops.
Nigerian example: Assuming there is network before a transfer. If not, it fails.
Illustration:
let x = Some(5); let y = x.unwrap(); // y = 5 let z: Option= None; // let w = z.unwrap(); // PANIC! Program crashes
Mini summary: unwrap and expect are quick but dangerous. Use them only when you are sure there is a value. In real programs, use match or ?.
? Operator – Safe Error PropagationDefinition: The ? operator returns the value if Ok, or returns the error if Err.
Why it is important: It makes error handling clean without match every time.
Simple explanation: If something works, continue. If it fails, pass the problem up to the caller.
Real-life example: A relay race. If a runner falls, the next runner takes over the problem.
School example: If you don’t understand a topic, ask the teacher. The teacher handles it.
Home example: If the remote is not working, give it to your dad. He fixes it.
Nigerian example: If the POS fails, the cashier calls the bank. The bank handles it.
Illustration:
fn read_number() -> Result{ let s = "42"; let n: i32 = s.parse().map_err(|_| "Not a number".to_string())?; Ok(n) }
Mini summary: The ? operator propagates errors cleanly. Use it in functions that return Result.
Definition: You can use structs and enums together to build powerful data models.
Why it is important: Real programs need both grouping and choices.
Simple explanation: A student struct with a field that is an enum for grade.
Real-life example: A car with a field for fuel type: Petrol, Diesel, Electric.
School example: A student with a field for result: Pass, Fail, Pending.
Home example: A delivery with a field for status: Ordered, Shipped, Delivered.
Nigerian example: A transaction with a field for payment method: Cash, Transfer, Card, USSD.
Illustration:
enum Grade {
A,
B,
C,
F,
}
struct Student {
name: String,
age: u32,
grade: Grade,
}
impl Student {
fn show(&self) {
println!("{} is {} years old", self.name, self.age);
match self.grade {
Grade::A => println!("Excellent!"),
Grade::B => println!("Good job!"),
Grade::C => println!("Keep trying!"),
Grade::F => println!("Needs improvement."),
}
}
}
Mini summary: Combine structs and enums for rich data. Use match to handle enum fields.
Definition: Traits are like abilities. Rust can automatically add some traits to your structs with #[derive(...)].
Why it is important: It saves you time and makes your structs work with printing, comparing, and cloning.
Simple explanation: Like a student automatically getting a library card when they join school. derive gives your struct automatic abilities.
Real-life example: A new phone automatically has calling and texting. You don’t build it from scratch.
School example: A new student automatically gets a uniform and timetable.
Home example: A new TV automatically has a remote.
Nigerian example: A new POS machine automatically accepts cards and prints receipts.
Illustration:
#[derive(Debug, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
let p1 = Point { x: 1, y: 2 };
let p2 = p1.clone();
println!("{:?}", p1); // Debug printing
Mini summary: #[derive(...)] adds traits like Debug, Clone, and PartialEq. It makes structs easier to use.
Definition: Mistakes happen. Knowing them helps you avoid them.
Why it is important: Rust’s compiler catches many mistakes. Learn from the messages.
Simple explanation: Like forgetting to fill a form correctly. Rust tells you what is missing.
Real-life example: Forgetting to sign a cheque. The bank rejects it.
School example: Forgetting to write your name on an exam. The teacher cannot mark it.
Home example: Forgetting to close the fridge. Food spoils.
Nigerian example: Forgetting to add the recipient’s name in a transfer. The money returns.
Table of common mistakes:
| Mistake | What happens | Rust’s help |
|---|---|---|
Forgetting to handle None | Compiler error | “non-exhaustive patterns” |
Using unwrap on None | Program panics | Runtime crash |
| Missing a field in struct | Compiler error | “missing field” |
| Wrong type in enum variant | Compiler error | “mismatched types” |
Forgetting &self in method | Compiler error | “expected &self” |
Mini summary: Rust catches many mistakes early. Read the error messages. They tell you exactly what to fix.
Definition: Best practices are good habits that make your code clean and safe.
Why it is important: Good habits make coding easier and more fun.
Simple explanation: Like keeping your room tidy, best practices keep your code tidy.
Real-life example: A chef keeps the kitchen clean. A programmer keeps code clean.
School example: Writing neatly in your notebook. Best practices make code readable.
Home example: Washing plates after eating. Best practices prevent problems later.
Nigerian example: Keeping your market stall organised. Best practices keep code organised.
List of best practices:
match to handle all variants.Option for missing values, not null.Result for operations that can fail.unwrap in real programs.? to propagate errors cleanly.Debug for easy printing.Mini summary: Good habits make you a better programmer. Start them now.
Let’s write a program that uses structs, enums, methods, and error handling.
Program: Student Report Card
#[derive(Debug)]
enum Grade {
A,
B,
C,
F,
}
struct Student {
name: String,
age: u32,
score: f64,
grade: Grade,
}
impl Student {
fn new(name: &str, age: u32, score: f64) -> Student {
let grade = if score >= 70.0 {
Grade::A
} else if score >= 60.0 {
Grade::B
} else if score >= 50.0 {
Grade::C
} else {
Grade::F
};
Student {
name: String::from(name),
age,
score,
grade,
}
}
fn show(&self) {
println!("Name: {}", self.name);
println!("Age: {}", self.age);
println!("Score: {}", self.score);
match self.grade {
Grade::A => println!("Grade: A (Excellent)"),
Grade::B => println!("Grade: B (Good)"),
Grade::C => println!("Grade: C (Average)"),
Grade::F => println!("Grade: F (Fail)"),
}
}
}
fn main() {
let s1 = Student::new("Ada", 13, 85.0);
let s2 = Student::new("Tunde", 14, 45.0);
s1.show();
println!("---");
s2.show();
}
Explanation:
Grade is an enum with four variants.Student is a struct with name, age, score, and grade.new is a method that creates a Student and calculates the grade.show is a method that prints the details and matches the grade.main creates two students and shows their reports.Mini summary: This program combines structs, enums, methods, and match. It is a complete example of what you can build.
| Word | Simple Definition |
|---|---|
| Struct | A custom type that groups related data. |
| Field | A piece of data inside a struct. |
| Instance | A specific value created from a struct. |
| Method | A function that belongs to a struct. |
impl | A block where you define methods for a struct. |
| Enum | A type that can be one of several variants. |
| Variant | One of the possible choices in an enum. |
Option | An enum for values that may be missing: Some or None. |
Result | An enum for success or failure: Ok or Err. |
unwrap | Extracts value or panics if empty. |
expect | Like unwrap but with a custom message. |
? operator | Propagates errors cleanly. |
| Trait | An ability or behaviour that types can have. |
derive | Automatically adds traits to a struct or enum. |
| Panic | When a program crashes due to an unrecoverable error. |
impl.match handles all enum variants safely.Option: Handles missing values without null.Result: Handles operations that can fail.? operator: Propagates errors cleanly.Debug and Clone automatically.struct Name { }.field: Type.Name { field: value, ... }.instance.field.impl Name { }.instance.method().Example:
struct Book {
title: String,
author: String,
pages: u32,
}
impl Book {
fn summary(&self) -> String {
format!("{} by {} ({} pages)", self.title, self.author, self.pages)
}
}
fn main() {
let b = Book {
title: String::from("Things Fall Apart"),
author: String::from("Chinua Achebe"),
pages: 209,
};
println!("{}", b.summary());
}
Result step by stepResult.Ok(value) for success.Err(error) for failure.match to handle both cases.? to propagate the error.Example:
fn parse_age(s: &str) -> Result{ match s.parse:: () { Ok(age) => Ok(age), Err(_) => Err(String::from("Invalid age")), } }
Option for optional fields.Result for success or error.Option for signal strength.Result for success or failure.Option for gold or empty.Result for success or failure.Option for milk or no milk.Result for success or burnt.Option and Result are inspired by functional programming languages.Option.? operator is one of Rust’s most loved features.Result is used everywhere in real programs?Option prevents the famous “billion-dollar mistake” of null pointers?match can destructure structs and enums?#[derive(Debug)] lets you print your structs easily?? operator can be used in functions that return Result?impl.match handles all enum variants.Option handles missing values.Result handles errors.unwrap is quick but risky.? propagates errors cleanly.derive adds traits automatically.None or Err.unwrap on a value that might be missing.Debug before printing.&self in methods.match.Option instead of null.Result for fallible operations.unwrap in real programs.? to propagate errors.Debug for easy printing.cargo check often.
struct Student {
name: String,
age: u32,
score: f64,
}
|
V
impl Student {
fn show(&self) { ... }
fn is_pass(&self) -> bool { ... }
}
|
V
let s = Student { ... };
s.show();
s.is_pass();
Start
|
V
Value exists?
|
+-- Yes --> Some(value)
|
+-- No --> None
|
V
Match on Option
|
+-- Some(v) --> Use v
|
+-- None --> Handle missing
Start
|
V
Operation succeeded?
|
+-- Yes --> Ok(value)
|
+-- No --> Err(error)
|
V
Match on Result
|
+-- Ok(v) --> Use v
|
+-- Err(e) --> Handle error
?
Function A returns Result
|
V
Calls Function B with ?
|
V
If B returns Ok --> continue
|
V
If B returns Err --> return Err to caller
|
V
Caller handles error
| Feature | Struct | Enum |
|---|---|---|
| Purpose | Group related data | Represent choices |
| Example | Student { name, age } | Grade { A, B, C, F } |
| Fields | All fields exist together | Only one variant at a time |
| Use case | Data records | State machines, options |
| Feature | Option | Result |
|---|---|---|
| Purpose | Value may be missing | Operation may fail |
| Variants | Some, None | Ok, Err |
| Use case | Optional fields | File reading, parsing |
| Error info | No error details | Contains error details |
| Method | Safety | When to use |
|---|---|---|
unwrap() | Risky (panics) | Quick tests, prototypes |
match | Safe | When you need custom handling |
? | Safe | When you want to propagate errors |
Lesson 1: Structs group related data.
Lesson 2: Create instances by filling fields.
Lesson 3: Methods are functions inside impl.
Lesson 4: Enums represent choices.
Lesson 5: Enum variants can hold data.
Lesson 6: match handles all enum variants.
Lesson 7: Option handles missing values.
Lesson 8: Result handles success and failure.
Lesson 9: unwrap and expect are quick but risky.
Lesson 10: The ? operator propagates errors cleanly.
Lesson 11: Combine structs and enums for rich data.
Lesson 12: derive adds traits automatically.
Lesson 13: Common mistakes include unhandled None and missing fields.
Lesson 14: Best practices: use Option, Result, and avoid unwrap.
Lesson 15: Combine structs, enums, methods, and error handling in a student report program.
Congratulations! You have finished Module Three. You learned how to build your own data types with structs and enums. You learned how to add methods to structs. You learned how to handle missing values with Option and errors with Result. You also learned the ? operator and how to derive traits like Debug. You saw many examples from Nigeria and everyday life. You now know common mistakes and best practices. Most importantly, you can design clean, safe programs that handle errors gracefully. Keep practising, and you will become a great Rust programmer!
Option? An enum for values that may be missing: Some or None.Result? An enum for success or failure: Ok or Err.unwrap? A method that extracts a value or panics.? operator? It propagates errors cleanly.derive? It automatically adds traits to a struct or enum.Option handles missing values safely.match.Match the word to its definition.
| Word | Definition |
|---|---|
| 1. Struct | A. A type that can be one of several variants. |
| 2. Enum | B. Groups related data. |
| 3. Method | C. Handles missing values. |
| 4. Option | D. Handles success or failure. |
| 5. Result | E. A function that belongs to a struct. |
Answers: 1-B, 2-A, 3-E, 4-C, 5-D
Option.Result.#[derive(Debug)].Title: “Build a School Management System”
Instructions: In groups of 3–4, design a Rust program that stores student information. Create a Student struct with name, age, and score. Create a Grade enum with A, B, C, F. Add a method to calculate the grade. Add a method to print the student details. Share your program with the class.
Goal: Practice structs, enums, methods, and match.
Task: Write a Rust program that defines a Book struct with title, author, and pages. Add a method summary() that returns a string. Create two books and print their summaries.
Hint: Use format! to build the string.
Project: “My Contact List”
Write a Rust program that defines a Contact struct with name, phone, and email (email is Option<String>). Create a few contacts. Add a method to print the contact details. If email is None, print “No email”. Use match to handle the Option.
Example output:
Name: Ada Phone: 08012345678 Email: ada@example.com --- Name: Tunde Phone: 08087654321 Email: No email
Assignment: Create a new Cargo project called bank_account. In main.rs, write a program that:
BankAccount struct with owner (String) and balance (f64).deposit(amount) and withdraw(amount) -> Result<(), String>.withdraw returns Err if there are insufficient funds.match to handle the Result from withdraw.Submit: Your main.rs file and a screenshot of the program running.
Option handles missing values.Result handles errors.unwrap is quick but risky.? propagates errors cleanly.derive adds traits automatically.Option instead of null?unwrap on None?? operator help with error handling?match important with enums?In Module Four, we will dive deeper into Rust. We will learn about:
To prepare, make sure you have completed the practical assignment and can write programs with structs, enums, and error handling. Review your notes on Option and Result. Bring your curiosity!
See you in Module Four!
End of Module Three – Rust Programming for Systems
Congratulations! You have completed all four modules of Rust Programming for Systems. This is a huge achievement. Let us look back at everything you have learned.
if, else if, and else.match for pattern matching.loop, while, and for.impl.Option.Result and the ? operator.
Module One
|
V
Module Two
|
V
Module Three
|
V
Module Four
|
V
Rust Programmer 🎉
With everything you have learned, you can now build:
Learning Rust does not stop here. To become a great programmer, you should:
You started this course not knowing anything about Rust. Now you can write programs that make decisions, repeat actions, store many values, handle errors, and organise code into modules. That is amazing!
Rust is used to build operating systems, web browsers, games, robots, and space satellites. One day, you could build something great with Rust too. Keep learning, keep building, and keep having fun.
Well done, and congratulations on completing the course!
End of Course – Rust Programming for Systems
```