Skip to content

Rust

To use varlock with Rust, install the standalone binary and run your app under varlock run. It loads and validates your env, then injects it into the process.

Add @generateRustEnv to your schema to generate a small, self-contained module: a serde-derived Env struct, a load() function that parses the injected __VARLOCK_ENV blob, and a SENSITIVE_KEYS constant.

.env.schema
# @generateRustEnv(path=src/env.rs)

The file is regenerated automatically on varlock load and varlock run, or explicitly with varlock codegen. It needs serde and serde_json:

Terminal window
cargo add serde --features derive
cargo add serde_json

Call load() once, then read fields off the struct. Required fields are the plain type, optional ones are Option<T>:

mod env;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let e = env::load()?; // call once, hold or pass `e` around
let host = e.db_host; // String (required field)
if let Some(port) = e.db_port { // Option<i64> (optional field)
println!("{host}:{port}");
}
Ok(())
}
Terminal window
varlock run -- cargo run

load() returns a clear error if __VARLOCK_ENV is missing (e.g. you forgot varlock run) or if a required key is absent. The struct derives a redacting Debug: sensitive fields print as <redacted>, so {:?} won’t leak secrets.

Every load() call re-parses the blob. If you want a process-wide instance, opt into a LazyLock yourself (loaded once, on first access; LazyLock needs Rust 1.80+, or use once_cell::sync::Lazy on older toolchains):

config.rs
use std::sync::LazyLock;
use crate::env;
pub static ENV: LazyLock<env::Env> =
LazyLock::new(|| env::load().expect("failed to load env"));
anywhere.rs
use crate::config::ENV;
let port = ENV.db_port; // Option<i64>

Prefer no globals? Call env::load()? once in main and pass &Env through your app state. The type comes along either way.