rust
46 lines · 7 steps
Loading typed config from environment variables in Rust
A Config struct is populated from env vars with typed parsing, defaults, and clear errors.
Explained by
highlit
1use std::env;
2use std::time::Duration;
3
4#[derive(Debug, Clone)]
5pub struct Config {
6 pub host: String,
7 pub port: u16,
8 pub database_url: String,
9 pub max_connections: u32,
10 pub request_timeout: Duration,
11 pub debug: bool,
12}
13
14impl Config {
15 pub fn from_env() -> Result<Self, String> {
16 Ok(Config {
17 host: env::var("HOST").unwrap_or_else(|_| "127.0.0.1".into()),
18 port: parse_var("PORT", 8080)?,
19 database_url: env::var("DATABASE_URL")
20 .map_err(|_| "DATABASE_URL must be set".to_string())?,
21 max_connections: parse_var("MAX_CONNECTIONS", 16)?,
22 request_timeout: Duration::from_secs(parse_var("REQUEST_TIMEOUT_SECS", 30)?),
23 debug: parse_bool("DEBUG", false),
24 })
25 }
26}
27
28fn parse_var<T>(key: &str, default: T) -> Result<T, String>
29where
30 T: std::str::FromStr,
31{
32 match env::var(key) {
33 Ok(raw) => raw
34 .trim()
35 .parse()
36 .map_err(|_| format!("invalid value for {key}: {raw:?}")),
37 Err(_) => Ok(default),
38 }
39}
40
41fn parse_bool(key: &str, default: bool) -> bool {
42 match env::var(key) {
43 Ok(raw) => matches!(raw.trim().to_ascii_lowercase().as_str(), "1" | "true" | "yes" | "on"),
44 Err(_) => default,
45 }
46}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Centralizing config in one struct with a single loader keeps environment access out of the rest of the codebase.
- 2A generic helper over FromStr lets one function parse any target type while supplying defaults and consistent errors.
- 3Returning Result with descriptive messages surfaces misconfiguration at startup instead of failing mysteriously later.
Related explainers
python
import time from concurrent.futures import ThreadPoolExecutor, as_completed from pathlib import Path
Parallel file downloads with a thread pool
concurrency
thread-pool
streaming-io
Intermediate
8 steps
rust
use std::collections::HashMap; #[derive(Debug, Clone)] struct Record {
Batched aggregation with fold in Rust
iterators
fold
hashmap
Intermediate
9 steps
java
package com.example.payments.config; import com.stripe.StripeClient; import org.springframework.beans.factory.annotation.Value;
Swapping payment gateways by profile in Spring
dependency-injection
profiles
configuration
Intermediate
6 steps
go
package validate import ( "fmt"
Struct validation with reflection in Go
reflection
struct-tags
validation
Intermediate
7 steps
ruby
class ParamCoercer TYPES = { integer: ->(v) { Integer(v) }, float: ->(v) { Float(v) },
Coercing request params by schema in Ruby
lambdas
type-coercion
lookup-table
Intermediate
7 steps
typescript
type AsyncFn<A extends unknown[], R> = (...args: A) => Promise<R>; interface MemoizeOptions<A extends unknown[]> { keyFn?: (...args: A) => string;
Memoizing async functions with TTL in TypeScript
memoization
generics
promises
Advanced
8 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/loading-typed-config-from-environment-variables-in-rust-explained-rust-0682/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.