Skip to content

Environment Variables Module

← Back to Standard Library

System environment variable access and manipulation.

Import

use <sys/env>

Overview

The env module provides functions for reading and modifying environment variables. It uses POSIX getenv() and setenv() functions for Unix portability across macOS, Linux, and other Unix-like systems.

Functions

getenv

Get an environment variable value.

fn getenv(string key) -> Maybe<string>

Parameters: - key - Environment variable name

Returns: - Maybe.Some(value) if variable exists - Maybe.None() if variable does not exist

Example:

use <sys/env>

fn main() i32:
    match getenv("PATH"):
        Maybe.Some(path) ->
            println("PATH: {path}")
        Maybe.None() ->
            println("PATH not set")

    return Result.Ok(0)

Common environment variables:

use <sys/env>

fn main() i32:
    # User information
    let Maybe<string> home = getenv("HOME")
    let Maybe<string> user = getenv("USER")

    # System paths
    let Maybe<string> path = getenv("PATH")
    let Maybe<string> tmpdir = getenv("TMPDIR")

    # Shell information
    let Maybe<string> shell = getenv("SHELL")

    # Display variables
    let Maybe<string> display = getenv("DISPLAY")

    if (home.is_some()):
        println("Home: {home.realise('')}")

    return Result.Ok(0)

setenv

Set an environment variable value.

fn setenv(string key, string value) -> Result<i32>

Parameters: - key - Environment variable name - value - New value to set

An existing variable with the same name is always overwritten.

Returns: - Result.Ok(0) on success - Result.Err() on failure (e.g., insufficient memory, invalid name)

Example:

use <sys/env>

fn main() i32:
    # Set a custom environment variable
    setenv("MY_APP_CONFIG", "/etc/myapp.conf")??

    # Verify it was set
    match getenv("MY_APP_CONFIG"):
        Maybe.Some(config_path) ->
            println("Config path: {config_path}")
        Maybe.None() ->
            println("Failed to set variable")

    return Result.Ok(0)

Overwriting an existing value:

use <sys/env>

fn main() i32:
    # Set an initial value
    setenv("MY_VAR", "initial")??

    # A second call always overwrites the existing value
    setenv("MY_VAR", "overwritten")??

    let string value = getenv("MY_VAR").realise("")
    println("MY_VAR: {value}")  # MY_VAR: overwritten

    return Result.Ok(0)

Error Handling

Both functions integrate with Sushi's error handling system:

getenv Error Handling

Since getenv returns Maybe<string>, use pattern matching or .realise():

use <sys/env>

fn main() i32:
    # With pattern matching
    match getenv("CONFIG_FILE"):
        Maybe.Some(path) ->
            println("Using config: {path}")
        Maybe.None() ->
            println("Using default config")

    # With .realise() for default value
    let string config = getenv("CONFIG_FILE").realise("/etc/default.conf")
    println("Config: {config}")

    # With .expect() for required variables
    let string required = getenv("REQUIRED_VAR").expect("REQUIRED_VAR must be set")

    return Result.Ok(0)

setenv Error Handling

Since setenv returns Result<i32>, use error propagation or pattern matching:

use <sys/env>

fn main() i32:
    # With error propagation (??)
    setenv("MY_VAR", "value")??

    # With explicit error handling
    let Result<i32, StdError> set_result = setenv("MY_VAR", "value")
    match set_result:
        Result.Ok(_) ->
            println("Variable set successfully")
        Result.Err(_) ->
            println("Failed to set variable")

    return Result.Ok(0)

Platform-Specific Behavior

macOS (darwin)

Platform-specific implementation in stdlib/src/_platform/darwin/env.py: - Uses standard POSIX getenv() and setenv() - Follows BSD semantics

Linux

Platform-specific implementation in stdlib/src/_platform/linux/env.py: - Uses standard POSIX getenv() and setenv() - Follows GNU/Linux semantics

Windows (partial support)

Windows support is planned but not yet fully implemented.

Security Considerations

Sensitive Data

Environment variables may contain sensitive information:

use <sys/env>

fn main() i32:
    # Be cautious when logging or displaying env vars
    let Maybe<string> api_key = getenv("API_KEY")

    # Don't print sensitive values
    if (api_key.is_some()):
        println("API key is configured")
        # Bad: println("API key: {api_key.realise("")}")

    return Result.Ok(0)

Validation

Always validate environment variable values:

use <sys/env>

fn is_valid_port(string port) bool:
    # Add validation logic
    return Result.Ok(true)

fn main() i32:
    match getenv("SERVER_PORT"):
        Maybe.Some(port) ->
            if (is_valid_port(port).realise(false)):
                println("Using port: {port}")
            else:
                println("Invalid port in SERVER_PORT")
        Maybe.None() ->
            println("Using default port: 8080")

    return Result.Ok(0)

Name Restrictions

Environment variable names should: - Contain only uppercase letters, digits, and underscores - Not start with a digit - Not contain = or null bytes

Invalid names will cause setenv to return Result.Err().

Example: Configuration from Environment

use <sys/env>
use <collections/strings>

struct Config:
    string host
    i32 port
    bool debug

fn load_config() Config:
    let string host = getenv("APP_HOST").realise("localhost")

    let string port_str = getenv("APP_PORT").realise("8080")
    let i32 port = port_str.to_i32().realise(8080)

    let string debug_str = getenv("APP_DEBUG").realise("false")
    let bool debug = debug_str == "true" or debug_str == "1"

    let Config config = Config(host, port, debug)
    return Result.Ok(config)

fn main() i32:
    let Config config = load_config().realise(Config("localhost", 8080, false))

    println("Host: {config.host}")
    println("Port: {config.port}")

    if (config.debug):
        println("Debug mode enabled")

    return Result.Ok(0)

Testing with Environment Variables

Test files can use setenv to set up test conditions:

use <sys/env>

fn test_env_vars() i32:
    # Setup test environment
    setenv("TEST_VAR", "test_value")??

    # Run tests
    let string value = getenv("TEST_VAR").realise("")

    if (value == "test_value"):
        println("Test passed")
    else:
        println("Test failed")

    return Result.Ok(0)

fn main() i32:
    return Result.Ok(test_env_vars().realise(1))

See Also