
Environment variable management is an important part of configuring applications. In Rust, we can load variables from a .env file and map them into a struct for type-safe access using the crates dotenvy, serde, and envy.
Install Dependencies
First, add the dotenvy crate to your project:
cargo add dotenvy
Then check your Cargo.toml to confirm it’s installed.
Create the .env File
Create a .env file in the project root directory:
ENVIRONMENT="development"
APP_USER="test"
PORT=555
Reading Environment Variables Manually
Here’s how you can load variables from the .env file using dotenvy and read them manually:
use std::env;
fn main() {
dotenvy::dotenv().ok(); // Load from .env
let environment = env::var("ENVIRONMENT").expect("ENVIRONMENT not set");
let app_user = env::var("APP_USER").expect("APP_USER not set");
let port = env::var("PORT").expect("PORT not set");
println!("ENVIRONMENT = {}", environment);
println!("USER = {}", app_user);
println!("PORT = {}", port);
}
The expect calls here handle errors in a fail-fast style, ensuring the variables exist and are ready to use.
Mapping to a Struct with serde
Instead of manually extracting each value, you can declare a Rust struct and derive Deserialize from serde to handle the data:
cargo add serde --features derive
This enables the required derive feature. Then:
serde = {version="1.0.228", features = ["derive"]}
use std::env;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
environment: String,
app_user: String,
port: u16,
}
fn main() {
dotenvy::dotenv().ok(); // Load from .env
let config = Config {
environment: env::var("ENVIRONMENT").expect("ENVIRONMENT not set"),
app_user: env::var("APP_USER").expect("APP_USER not set"),
port: env::var("PORT").expect("PORT not set"),
};
println!("ENVIRONMENT: {}", config.environment);
println!("APP_USER: {}", config.app_user);
println!("PORT: {}", config.port);
}
This method still manually maps values but benefits from typed fields thanks to serde.
Automatically Mapping with envy
To avoid mapping environment variables one by one, you can use the envy crate. It automatically deserializes environment variables into a struct.
cargo add envy
Then use it like this:
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Config {
environment: String,
app_user: String,
port: u16,
}
fn main() {
dotenvy::dotenv().ok(); // Load from .env
let config: Config = envy::from_env().expect("Failed to load config");
println!("{:?}", config.app_user);
println!("{:?}", config.environment);
println!("{:?}", config.port);
}
⚠️Warning!!
Before using envy, make sure your environment variable names match your struct fields.
Note & Summary
- dotenvy loads environment variables from a .env file into your Rust app.
- envy maps those environment values to a Rust struct.
- serde handles the deserialization and type conversion for your struct fields.
This combination keeps your configuration clean and type-safe, which is especially useful as the number of environment variables grows.
You can find the full example and source code in this GitHub repository: code


Top comments (0)