
Rust: How to read environment variables
This is one of the examples I š about this approach to learning a new systems language. Itās already forcing me down the rabbit-hole of understanding so much about what makes Rust interesting. Hereās the simplest possible way I could find to get the value of an environment var:
use std::env;
fn main() {
let key = "HOME";
let val = env::var(key).unwrap();
println!("{}: {:?}", key, val); # /Users/glenngillen
}
Import the relevant stdlib
, call the var
function, all pretty standard so far. But thereās that curious unwrap()
call on there too. Whatās that about? Well it turns out most Rust functions return a Result
type (or similar Option
type) rather than just a value. Much like in golang (though there itās more of an idiom to return a tuple of values rather than a type of its own) youāre expected to explicitly handle both the success and failure case and not just assume everything is always successful.
unwrap()
is kinda cheating here, and asking Rust to just give me the value from the success result (i.e., āunwrapā the result type to give me the value). So what happens if we try to access an environment variable that is not set:
use std::env;
fn main() {
let key = "NOPE";
let val = env::var(key).unwrap(); # This will panic
println!("{}: {:?}", key, val); # We'll never get here
}
we get this:
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 1.21s
Running `target/debug/playground`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: NotPresent', src/main.rs:15:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
ruh-roh! We canāt have our whole app panic just because an environment variable is not set!
But before we get to that a brief diversion: while calling unwrap()
in this particular example is poor form, thereās plenty of scenarios where itās not just acceptable but would produce simpler and more maintainable code. So much so that thereās an operator to make achieving a similar result easier: ?
. Similar is doing a lot of heavy lifting here, as I couldnāt just replace the use of unwrap()
with ?
in this example. You can only use ?
on Result
or Option
types, and it also returns an Err
automatically too. Which means the function calling it needs to be capable of returning an error result, which our current main()
does not. Thankfully modern versions of Rust (anything >= 1.26.0) allow main()
to return a result. So a quick update of our example to return a result, including the error type from a failed variable lookup:
use std::env;
fn main() -> Result<(), std::env::VarError> {
let key = "NOPE";
let val = env::var(key)?;
println!("{}: {:?}", key, val);
Ok(())
}
All that was just to highlight how to use ?
instead of unwrap()
. Given weāre going to be handling the error case explicitly weāll revert back to the original main()
definition for the rest of that. And with that, back to a more robust way of dealing with the scenario that an environment variable is not set.
Rust has the match
keyword which can be used as a form of flow control like a switch
statement. You can also use it as a way to handle the success or error cases for a result, with Ok()
and Err()
respectively:
use std::env;
fn main() {
let key = "NONE";
match env::var(key) {
Ok(val) => println!("{}: {:?}", key, val),
Err(e) => println!("couldn't interpret {}: {}", key, e),
}
}
Here you can see that weāre passing the Result
from our call to env::var()
into match
. Then thereās two branches. For the Ok()
result, take the val
and pass it into the println!
call we had earlier. Now for the Err()
case we catch the error raised (e
) and print out more descriptive output of what went wrong. The result now is:
couldn't interpret NONE: environment variable not found
Published: 23/03/2022
I've spent most of my career working with or at startups. I'm currently the VP of Product / GTM @ Ockam where I'm helping developers build applications and systems that are secure-by-design. It's time we started securely connecting apps, not networks.
Previously I led the Terraform product team @ HashiCorp, where we launched Terraform 1.0, Terraform Cloud, and a whole host of amazing capabilities that set the stage for a successful IPO. Prior to that I was part of the Startup Team @ AWS, and earlier still an early employee @ Heroku. I've also invested in a couple of dozen early stage startups.