CSV Storage and Persistence
The Weather App allows users to save cities such as Berlin, Munich, These cities should remain available even after the application is closed.
To achieve this, the Weather App stores city names in a simple CSV file.
Instead of using a database, the app uses a lightweight text file. This makes the storage system easy to understand and implement, especially for beginners.
CSV File Structure
The storage file is defined using a constant:
#![allow(unused)] fn main() { const CSV_FILE: &str = "saved_cities.csv"; }
This file stores one city per line.
Example:
Berlin
Munich
Paris
There are no commas or extra columns. Each line simply represents one city name.
This simple structure makes the file:
- easy to read
- easy to debug
- easy to edit manually if needed
Why a Simple CSV Design Is Used
The Weather App only needs to store city names.
It does not store:
- latitude
- longitude
- timestamps
- weather data
These values are fetched dynamically from the weather API when needed. Because of this, a simple text file is enough. This design helps beginners understand persistence without needing databases or complex file formats.
In-Memory Storage
While the application is running, the cities are stored in memory using a vector:
Vec<String>
Example:
["Berlin", "Munich", "Paris"]
This vector is the main working data structure used by the application.
The CSV file is only used to save and restore this data when the app starts or closes.
Loading Cities When the App Starts
When the Weather App starts, it reads the CSV file and loads the cities into memory.
#![allow(unused)] fn main() { fn load_cities_from_csv() -> Vec<String> { if !Path::new(CSV_FILE).exists() { return vec![]; } let Ok(text) = fs::read_to_string(CSV_FILE) else { return vec![]; }; let mut out = Vec::new(); for line in text.lines() { let city = line.trim(); if !city.is_empty() && !out.iter().any(|c| c.eq_ignore_ascii_case(city)) { out.push(city.to_string()); } } out } }
This function performs several steps. First, it checks if the file exists. If the file does not exist, the application simply starts with an empty list. Next, the file is read as plain text. Each line represents a city. The function removes extra spaces and avoids duplicates before adding cities to the vector. At the end, the function returns the list of cities stored in memory.
Saving Cities After Changes
Whenever a user adds or removes a city, the CSV file must be updated.
The Weather App saves the cities using this function:
#![allow(unused)] fn main() { fn save_cities_to_csv(cities: &Vec<String>) { let mut buf = String::new(); for c in cities { buf.push_str(c); buf.push('\n'); } let _ = fs::write(CSV_FILE, buf); } }
This function converts the vector of cities into plain text. Each city is written on its own line. The entire file is rewritten every time the list changes. This approach keeps the logic simple because the file always matches the current state of the vector.
How Storage Works in the Weather App
The Weather App follows a simple data flow.
1. Application Start
The CSV file is read and converted into a vector.
CSV file → Vec<String>
2. User Adds or Removes Cities
The vector is updated.
Vec<String> updated
3. File Is Updated
The vector is written back to the CSV file.
Vec<String> → CSV file
This means:
- The vector in memory is the active state
- The CSV file only stores the data permanently
Connection to the Sidebar
The sidebar list in the Weather App displays the cities stored in the vector.
When a user adds a city:
- The city is added to the vector
- The CSV file is updated
- The sidebar list is rebuilt
When a user deletes a city:
- The city is removed from the vector
- The CSV file is updated
- The sidebar refreshes
This keeps the UI and storage synchronized.
Why This Design Is Good for Beginners
This storage system is intentionally simple.
It teaches beginners how persistence works without introducing complex technologies.
From this example, a beginner learns how to:
- store application data in a file
- read file content at startup
- keep data in memory using vectors
- update the file when data changes
The storage logic is also separated from the UI logic. The CSV functions only handle reading and writing data. They do not interact with GTK widgets.