Skip to content

std/env ​

Environment variable access.

milo
from "std/env" import { Env }

Example ​

milo
match Env.get("HOME") {
    Some(home) => print(home),
    None => print("HOME not set"),
}
let port = Env.getOr("PORT", "8080")
let _ = Env.set("RUST_LOG", "debug")

Env.set and Env.remove change this process's environment, and they are not thread-safe: set the environment during startup, before spawning anything. See Env.set below for the details.

API reference ​

Env ​

milo
pub struct Env

Process environment operations grouped for discoverability.

Env.get ​

milo
fn Env.get(name: string): Option<string>

Look up an environment variable. None if it is unset.

Env.getOr ​

milo
fn Env.getOr(name: string, defaultVal: string): string

Look up an environment variable, or defaultVal if it is unset: Env.getOr("PORT", "8080").

Env.remove ​

milo
fn Env.remove(name: &string): Result<Unit, EnvError>

Remove name from THIS process's environment. Removing a name that is not set succeeds. Carries the same thread-safety caveat as set — see there.

Env.set ​

milo
fn Env.set(name: &string, value: &string): Result<Unit, EnvError>

Set name to value in THIS process's environment, replacing any current value. Children spawned afterwards inherit it; the parent shell never sees it.

NOT thread-safe, and the platform gives no way to make it so. C's setenv may free the block a concurrent getenv is reading, so a getenv on another OS thread can read freed memory — a real crash, not a stale read. What Milo does and does not promise:

  • Safe from a single-threaded program, and from anywhere in a program whose green tasks all run on the scheduler's one OS thread: green tasks only switch at await points, and this call contains none.
  • NOT safe concurrently with Promise.blocking work, which runs on real OS threads and may call Env.get (or any C library that calls getenv) at the same instant.
  • Env.get itself copies the value out before returning, so a value already read is never invalidated; the window is only the read in progress.

The safe pattern is the one Go and Rust also recommend: set the environment during startup, before spawning anything.

To give one child a variable without changing your own environment, use Command.env in std/process instead.

EnvError ​

milo
pub enum EnvError

Why a mutation can fail. No catch-all variant: these are the only two outcomes libc distinguishes for setenv/unsetenv.

Variants:

  • InvalidName: Empty name, or a name containing '='. libc reports EINVAL; the check is done here so the reason is the same on every platform.
  • Failed: The C call refused it — in practice out of memory.