This article will be a departure from my usual blockchain-themed articles. Recently I got back into Rust development after almost a year away from it. The first thing that came to my mind is how do I use environment variables in Rust?
A bit of context here – I have some experience in Web Dev and using
dotenv for reading environment variables from a file is a niche thing in almost all projects which use JS nowadays. You need not be a web developer. Even a blockchain developer would have come across such a use case. Hardhat, for instance, makes you define the environment variables which are then read in the
hardhat.config.js file. This sets the Hardhat local development environment while testing and also the required environment variables while deploying a contract.
I came across this awesome Rust framework called Tide. It is perhaps among a handful of projects which are production grade at the time of writing. Rust is very new compared to JS and still, the growth in projects and community in the Rust ecosystem is happening at a break-neck speed. So perhaps in a year, there might be other production-ready frameworks.
Anyways, my motivation basically comes from the above experience with handling Env Variables in JS and I was searching for a crate. I knew the Rust standard library is powerful AF so I was searching for a way there as well. This blog is what came out of all that. Considering my efforts, I thought it might be prudent to share my way with others who might be searching for a similar option to handle environment variables in Rust.
Turns out you need only 13-ish lines to accomplish what
dotenv does in JS. Ofcourse, you could go ahead and make it more robust by writing code to handle environment variables by reading different files in different scenarios.
You could have a
.env.prod for your developmental, staging and production stages. You could pass in a flag environment variable while running the project like say
ENV=DEV cargo run //Rest of the args. That’s totally cool. The code below is a bare minimum.
Upto line 4 we are importing the stuff. Here you can see the Rust Standard Library at play. The
set_env function takes a filename (
.env file) and then returns a Result type. If you are a beginner in Rust, the
Result type from
std is an enum that can either be of
Ok type or an
Err. You might also notice that we are wrapping the Error by a Box pointer. This helps with interior mutability. It is a fix to help return multiple specific type of errors.
read_to_string() method takes in the filename. This method, by default, searches the file in the project root. It returns the contents of the file in a string blob. In our case, the environment variable file will follow the standard pattern and have the format
<name>=<value> with each name in a new line. So if we split the string blob using “\n”, that should give us a list where every element is a string of format =. That’s just simple logic at play. The
split() method returns an iterator and so we can easily iterate through those with the for loop.
Inside the for loop we are again splitting each line at the “=” character. We then collect that in a Vector of strings (string references to be exact). This means we have the name of the environment variable at 0th index and the value at the 1st index.
env::set_var() takes two arguments. The first is the environment variable name and the second is the value of that variable. We set every environment variable specified in the filename passed to the function. At the last line, we return
Ok(()) to signify that the function execution has been completed successfully.
When you run the function, the environment variables persist till the program ends execution. This can be when you stop the server running on a specific port. You will have access to those environment variables anywhere in the app using
env::var(<variable name>). Keep in mind this returns a
Result type so you need to unwrap or use match-based pattern matching.
And that’s it. It is as easy as that. You can use the code or modify it as you see fit. If you have advanced knowledge in rust, one thing you might still be wondering about is why the Box Pointer if there is only one type of error. The simple answer is that you can try to use
join_paths() in the code above as required. When that happens, you would notice the use of
? to detect the result of the functions will be allowed. All thanks to the Box.
I might go on to build something with Tide in the next article or perhaps not (I am whimsical like that XD). Either way, if you liked this article, consider clicking the follow button and leaving a reaction. If you think this article could have been better – I’d love it if you start a discussion below regarding the same. Until the next one, continue to build awesome stuff and WAGMI!