rust
47 lines · 9 steps
Parsing a config file with typed errors in Rust
A key=value parser that reports failures as a custom error enum instead of panicking or returning strings.
Explained by
highlit
1use std::collections::HashMap;
2use std::fmt;
3
4#[derive(Debug)]
5pub enum ParseError {
6 MissingSeparator { line: usize, content: String },
7 EmptyKey { line: usize },
8}
9
10impl fmt::Display for ParseError {
11 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
12 match self {
13 ParseError::MissingSeparator { line, content } => {
14 write!(f, "line {line}: missing '=' in `{content}`")
15 }
16 ParseError::EmptyKey { line } => write!(f, "line {line}: empty key"),
17 }
18 }
19}
20
21impl std::error::Error for ParseError {}
22
23pub fn parse_config(input: &str) -> Result<HashMap<String, String>, ParseError> {
24 let mut config = HashMap::new();
25
26 for (idx, raw) in input.lines().enumerate() {
27 let line = idx + 1;
28 let trimmed = raw.trim();
29
30 if trimmed.is_empty() || trimmed.starts_with('#') {
31 continue;
32 }
33
34 let (key, value) = trimmed.split_once('=').ok_or_else(|| {
35 ParseError::MissingSeparator { line, content: trimmed.to_owned() }
36 })?;
37
38 let key = key.trim();
39 if key.is_empty() {
40 return Err(ParseError::EmptyKey { line });
41 }
42
43 config.insert(key.to_owned(), value.trim().to_owned());
44 }
45
46 Ok(config)
47}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Modeling failure as an enum lets each error variant carry exactly the context callers need.
- 2Implementing Display and Error makes a custom type behave like any standard error in the ecosystem.
- 3Combining ok_or_else with ? turns an Option into a typed error and propagates it in one line.
Related explainers
rust
use std::path::PathBuf; use clap::{Parser, Subcommand, ValueEnum};
Building a CLI with clap's derive macros
cli
derive-macros
argument-parsing
Intermediate
10 steps
rust
use std::sync::Arc; use axum::{ body::Body, extract::{Extension, State},
Passing per-request context through Axum middleware
middleware
request-context
dependency-injection
Intermediate
8 steps
java
public final class QueryParams { private final Map<String, List<String>> params;
Parsing and building URL query strings in Java
parsing
multimap
url-encoding
Intermediate
9 steps
rust
use chrono::{DateTime, Duration, FixedOffset, Utc}; #[derive(Debug)] pub struct EventWindow {
Parsing and measuring time windows in Rust
datetime
error-handling
pattern-matching
Intermediate
7 steps
rust
use axum::{ extract::Path, http::{header::ACCEPT, HeaderMap, StatusCode}, response::{Html, IntoResponse, Json, Response},
Content negotiation in an Axum handler
content-negotiation
http-headers
serialization
Intermediate
8 steps
rust
use axum::{extract::Multipart, http::StatusCode, Json}; use serde::Serialize; use tokio::io::AsyncWriteExt; use uuid::Uuid;
Streaming multipart uploads in Axum
multipart
streaming
async-io
Intermediate
10 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/parsing-a-config-file-with-typed-errors-in-rust-explained-rust-76a8/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.