Rust
Summary - Serde
Combine serde_json and derive to build a config reader.
Serde summary
#[derive(Serialize, Deserialize)] defines the data structure independently of the format; a crate like serde_json provides the format (to_string, from_str). Names are mapped with rename/rename_all. Missing fields are tolerated with Option or #[serde(default)]; skip_serializing_if skips empty fields. Reading always returns a Result, and the error message names the missing or invalid field.
Round trip
Two functions that write a settings type and read it back from the same text:
#[derive(Debug, PartialEq)]
struct Ayar {
port: u16,
hata_ayikla: bool,
}
impl Ayar {
fn json(&self) -> String {
format!("{{\"port\":{},\"hata_ayikla\":{}}}", self.port, self.hata_ayikla)
}
fn oku(s: &str) -> Result<Ayar, String> { /* parse key by key */ }
}
The reader must convert each key by its type and give a clear error on an unknown key.
Exercise Settings round trip
Write the json and oku methods for the Ayar type above (oku: drop the braces, split at commas, split_once(':'), parse by key). Print the JSON for Ayar { port: 8080, hata_ayikla: false }, then read it back and print with {:?}.
Show example solution
#[derive(Debug, PartialEq)]
struct Ayar {
port: u16,
hata_ayikla: bool,
}
impl Ayar {
fn json(&self) -> String {
format!("{{\"port\":{},\"hata_ayikla\":{}}}", self.port, self.hata_ayikla)
}
fn oku(s: &str) -> Result<Ayar, String> {
let mut port = None;
let mut hata_ayikla = None;
for cift in s.trim().trim_start_matches('{').trim_end_matches('}').split(',') {
let (a, d) = cift.split_once(':').ok_or("geçersiz çift")?;
let deger = d.trim();
match a.trim().trim_matches('"') {
"port" => port = Some(deger.parse().map_err(|_| "geçersiz port")?),
"hata_ayikla" => hata_ayikla = Some(deger.parse().map_err(|_| "geçersiz bool")?),
k => return Err(format!("bilinmeyen anahtar: {k}")),
}
}
Ok(Ayar {
port: port.ok_or("port eksik")?,
hata_ayikla: hata_ayikla.ok_or("hata_ayikla eksik")?,
})
}
}
fn main() {
let a = Ayar { port: 8080, hata_ayikla: false };
let metin = a.json();
println!("{}", metin);
println!("{:?}", Ayar::oku(&metin).unwrap());
}
Test yourself
-
Why is writing a config and reading it back to get the same value (round-trip) an important test?
- It proves the writing and reading code agree with each other
- It measures performance
- It isn't needed
- It shrinks the file size
Show answer
It proves the writing and reading code agree with each other — It's especially valuable for hand-written serialization; even with derive it shows attributes like rename work correctly.
-
Read the boolean
"hata_ayikla" => hata_ayikla = deger.____().map_err(|_| "geçersiz bool")?,
Show answer
"hata_ayikla" => hata_ayikla = deger.parse().map_err(|_| "geçersiz bool")?,
-
Group summary
- to_string
- Writing
- from_str
- Reading
- rename_all
- Naming rule
- skip_serializing_if
- Skip empty
Next lesson: Model skeleton