A tiny Rust macro for safe env var parsing
Every Rust service I write ends up with a block of std::env::var("FOO").ok().and_then(...) chains that all look the same. A tiny macro_rules! cleans that up. It supports two forms: required (panics on startup with a clear message) and default (parses the env var if present, otherwise uses the default).
It is not magical. It is the same .parse::<T>()? you would have written, just without the boilerplate.
macro_rules! env_var {
($name:literal: $ty:ty) => {{
let s = std::env::var($name)
.unwrap_or_else(|_| panic!("missing env var {}", $name));
s.parse::<$ty>()
.unwrap_or_else(|e| panic!("bad env var {}: {}", $name, e))
}};
($name:literal: $ty:ty = $default:expr) => {{
match std::env::var($name) {
Ok(s) => s.parse::<$ty>()
.unwrap_or_else(|e| panic!("bad env var {}: {}", $name, e)),
Err(_) => $default,
}
}};
}
fn main() {
let port: u16 = env_var!("PORT": u16 = 8080);
let max: usize = env_var!("MAX_CONNS": usize);
let name: String = env_var!("SERVICE_NAME": String = "api".into());
println!("{name} on :{port}, max {max}");
}
Caveats: I use this only at startup, so panics are fine. For anything runtime-reloadable, return a Result. See also /posts/result-and-option-ergonomics-after-six-months/.