Chapter 03: Quest App: Saving and Loading Your Adventures (Reading from a File)
published:
Hi there, Super Programmer!
You've done an amazing job so far! Your Quest App can now take new quests from your commands and show you a list of quests. But there's a tiny problem: every time you close the program, all your quests disappear! That's not very helpful, is it?
Let's fix that by learning how to save your quests to a file and load them back when your program starts! This way, your quests will always be there, just like notes in a magical notebook.
Our Quest File: quests.txt
We're going to create a simple text file named quests.txt. Each line in this
file will be one of your quests.
Before you continue, create a new file named quests.txt in your project
folder (next to src and Cargo.toml).
Inside quests.txt, you can write quests like this:
Explore the mysterious forest
Learn a new Rust command
Find the hidden treasure
Bake a delicious cakeHow Rust Reads Your File
Rust has special tools to read files from your computer. We'll use
std::fs::read_to_string to read the entire file, and then we'll split it into
individual quests.
Here's how we can update our main.rs to load quests from quests.txt:
use std::fs; // We need to 'use' the filesystem tools!
fn main() {
// --- Part 1: Load Quests from quests.txt ---
let mut quests: Vec<String> = Vec::new();
// Try to read the quests.txt file
let quests_file_content = fs::read_to_string("quests.txt");
match quests_file_content {
Ok(content) => {
// If the file was read successfully, split the content by lines
// and add each line as a quest.
for line in content.lines() {
if !line.trim().is_empty() { // Make sure we don't add empty lines
quests.push(line.to_string());
}
}
println!("Loaded {} quests from quests.txt!", quests.len());
},
Err(error) => {
// If there was an error (like the file not existing),
// we'll just say so and start with an empty quest list.
println!("Could not read quests.txt: {}", error);
println!("Starting with an empty quest list. Make sure quests.txt exists!");
}
}
// --- Part 2: (Optional) Add a New Quest from Command-Line Arguments ---
let args: Vec<String> = std::env::args().collect();
let mut new_quest_description: Option<String> = None;
if args.len() > 1 {
new_quest_description = Some(args[1..].join(" "));
}
if let Some(quest_text) = new_quest_description {
quests.push(quest_text);
println!("Awesome! We added your new quest.");
// We'll learn how to save this new quest permanently in the next chapter!
} else {
println!("To add a new quest, type 'cargo run' followed by your quest, like:");
println!(" cargo run find the golden apple");
}
// --- Part 3: Display All Quests ---
println!("");
println!("--- Your Current Quests ---");
if quests.is_empty() {
println!("No quests found! Time to add some new adventures!");
} else {
for (index, quest) in quests.iter().enumerate() {
println!("{}. {}", index + 1, quest);
}
}
println!("---------------------------");
}What's New Here?
-
use std::fs;- This line tells Rust that we want to use the tools for working with the "filesystem" (files and folders).
-
let quests_file_content = fs::read_to_string("quests.txt");- This is the magical line that tries to read the entire
quests.txtfile and puts its contents intoquests_file_content. read_to_stringdoesn't just give us the content; it gives us aResult! ThisResultcan beOk(content)(meaning it worked, andcontenthas the text) orErr(error)(meaning something went wrong, anderrortells us what).
- This is the magical line that tries to read the entire
-
match quests_file_content { Ok(content) => { ... }, Err(error) => { ... } }- The
matchstatement is like a super-smartif/elsethat handles theResult. - If
Ok(content), we take thecontent(the text from the file). for line in content.lines() { ... }We then go through eachlinein the file content.if !line.trim().is_empty()This checks if the line isn't just empty spaces before adding it.quests.push(line.to_string());And finally, we add each valid line as a quest to ourquestslist.- If
Err(error), we print a friendly message telling us what went wrong.
- The
-
Combining with Previous Lessons: Notice how we've put together everything we learned!
- We first load quests from the file.
- Then, we check if you gave us a new quest using command-line arguments.
- Finally, we print out all the quests, both loaded from the file and any new one you added!
-
println!("{}. {}", index + 1, quest);- We've added
enumerate()to our loop to give each quest a number, making our list look even better!
- We've added
Your Turn
-
First, create a
quests.txtfile in your main project folder and add a few quests to it (one per line). -
Update your
src/main.rsfile with the full code example provided above. -
Run your program:
cargo runYou should see all the quests from your
quests.txtfile printed out! -
Try adding a new quest:
cargo run defeat the evil wizardYou'll see your
quests.txtquests plus your new quest! (Even though it won't save to the file yet, we'll get to that later!)
You're building a real app now! Keep up the amazing work!