Mini Post: Parsing Environment Variables Safely in Rust
Almost every app I write nowadays uses an environment file, and therefore I always have to read things from the environment and parse them into different forms. Doing this safely with defaults in Rust was a bit tricky for me at first, but I’ve landed on a solution I like.
Optional Numbers
The reason I was looking for code like this was to try and get a port for a database from the environment with a reasonable fallback. Here’s what I landed on:
main.rs
use std::env;
use dotenv::dotenv;
fn main() {
dotenv().ok();
let count = env::var("COUNT")
// convert to Option<String>
.ok()
// convert to Option<u64> via parse
.and_then(|value| value.parse::<u64>().ok())
// Use 5 as a default if either the value was not found or it could
// not be converted.
.unwrap_or(5)
println!("{}", count * 5);
}
Default Strings
Of course, you can always just get a required value like this:
main.rs
std::env;
use dotenv::dotenv;
fn main() {
dotenv().ok();
let test = env::var("TEST")
.unwrap_or(String::from("Test"));
println!("{}", test);
}
Required Environment Variables
And of course, you can make them required by just unwrap
ing them, but that
will crash your program:
main.rs
std::env;
use dotenv::dotenv;
fn main() {
dotenv().ok();
let test = env::var("TEST").unwrap();
println!("{}", test);
}
Output
thread 'main' panicked at src\main.rs:6:35:
called `Result::unwrap()` on an `Err` value: NotPresent
You can instead print a helpful message:
main.rs
std::env;
use dotenv::dotenv;
fn main() {
dotenv().ok();
let test = env::var("TEST");
match test {
Ok(test) => {
println!("{}", test)
}
Err(_) => {
eprintln!("Missing required environment variable 'TEST'");
return;
}
}
}
Output
Missing required environment variable 'TEST'