Skip to content

Environment Variables (std::env)

std::env follows the 12-factor model: configuration comes from the environment. The module offers reading and writing individual variables, enumeration of the full environment, and platform functions such as env.os() and env.arch(). Because it interacts with the host process, it cannot be run in the WASM sandbox; use the Zolo CLI.


Read, set, and remove variables

env.get(name) returns the value string or nil when the variable does not exist. The ?? operator is the natural idiom for defaults. env.set and env.remove modify the current process's environment:

Reads PATH, applies a default with ??, sets APP_NAME, and then removes it.

01-get-set.zolo
// Feature: env.get / env.set — environment variables
// When to use: env-driven config (12-factor), feature flags, secrets.

use std::env

// env.get(name) -> string or nil if not present.
let path = env.get("PATH")
print(path != nil)  // expected: true

let missing = env.get("VARIABLE_THAT_DOES_NOT_EXIST_42")
print(missing)  // expected: nil

// Idiomatic pattern: default via `??`.
let port = env.get("APP_PORT") ?? "8080"
print(port)  // expected: 8080 (if APP_PORT not set)

// env.set(name, value) modifies the current process environment.
env.set("APP_NAME", "zolo")
print(env.get("APP_NAME"))  // expected: zolo

// env.remove undoes it.
env.remove("APP_NAME")
print(env.get("APP_NAME"))  // expected: nil

Requires the Zolo CLI/host — open in the playground or run locally.


Enumerate the environment and query the system

env.all() returns a map of all variables. env.os(), env.arch(), and env.home_dir() expose OS information useful in cross-platform scripts:

Reads PATH from env.all(), prints OS, architecture, and home directory.

02-all-and-info.zolo
// Feature: env.all / env.os / env.arch / env.home_dir — environment info
// When to use: cross-platform scripts, debugging, picking paths per OS.

use std::Map
use std::env

// env.all() -> plain table mapping VAR=value. Use Map.from to wrap
// it in a Map object, or index it directly with `[name]`.
let all = env.all()
let path = all["PATH"]
let has_path = path != nil
print(has_path)  // expected: true

// Current OS: "windows", "linux", "macos".
// (`os` shadows the Lua-builtin module of the same name; pick a
// different identifier to be safe.)
let cur_os = env.os()
print(cur_os.len() > 0)  // expected: true

// Architecture: "x64", "arm64", etc.
let cur_arch = env.arch()
print(cur_arch.len() > 0)  // expected: true

// User's home directory (nil only on very unusual setups).
let home = env.home_dir()
let has_home = home != nil
print(has_home)  // expected: true

Requires the Zolo CLI/host — open in the playground or run locally.


Pattern: configuration struct

The recommended pattern for 12-factor applications is to centralise all defaults in a Config struct and load it once at startup, with helper functions to convert strings to integers and booleans:

Defines Config with port, log_level, debug, and database_url via environment variables.

03-config-pattern.zolo
// Feature: env — idiomatic pattern for loading configuration
// When to use: 12-factor applications read config from env vars.

use std::string
use std::env

// Define a config struct to centralize all defaults.
struct Config {
  port: int,
  log_level: str,
  debug: bool,
  database_url: str,
}

fn parse_int(s: str?, default_v: int) -> int {
  if s == nil { return default_v }
  let n = string::to_int(s)
  if n == nil { return default_v }
  return n
}

fn parse_bool(s: str?) -> bool {
  if s == nil { return false }
  return s == "true" || s == "1" || s == "yes"
}

fn load_config() -> Config {
  return Config { port: parse_int(env.get("APP_PORT"), 8080), log_level: env.get("APP_LOG_LEVEL") ?? "info", debug: parse_bool(env.get("APP_DEBUG")), database_url: env.get("DATABASE_URL") ?? "postgres://localhost/dev" }
}

let cfg = load_config()
print(cfg.port)  // expected: 8080 (default)
print(cfg.log_level)  // expected: info (default)

Requires the Zolo CLI/host — open in the playground or run locally.

Challenge

Set APP_PORT=3000 before running and verify that cfg.port changes from 8080 to 3000.

enespt-br