Axle v0.14.1

Standard library overview

Every public symbol in Axle’s standard library is reachable from user code via use:

use std::collections::ArrayList;     // import one symbol (a type)
use std::text;                        // or bind the module: text::parseI32("42")
use std::numeric::random;             // bind, then call random::nextI64()

How the library is shaped

Three recurring shapes cover almost every module. Recognising them tells you how to call a symbol before you open its reference page.

  • Free functions — a module exposes plain functions you call module-qualified: text::parseI32("42"), encoding::base64Encode(s), datetime::formatIso8601(ms). Bind the module once (use std::text;) and reach every function through it — text::parseI32(…), never a one-function-per-line import list. When a function’s signature mentions a module-local enum (for example log::setLevel, which takes a LogLevel), add a second use for the enum type only — use std::system::log; use std::system::log::LogLevel; — and keep calling through the module: log::setLevel(LogLevel::INFO). The type is imported; the functions never are.

  • Façade classes — a class with only static methods and no instance state, reached through ::: Random::seed(42), Console::println("hi"), Float::toString(1.0). A façade is just a namespace for related operations — there is nothing to construct. The prelude classes (Console, Random, Async, ProcessBuilder, Process, Float, Char) need no use.

    A façade earns its place by holding state between calls, or by wrapping a receiver the method surface cannot reach. Operations over a plain value are methods on the value itself rather than a façade — see Methods on primitives below.

  • Handle-backed classes — a class wrapping a native resource behind a single handle : i64 field: file streams, Pattern, Process, the net sockets. Most are constructed directly with new Foo(...); a few are only ever handed to you, either by a static factory (BufferedReader::open(path), FileInputStream::fromFile(f), ByteArrayInputStream::fromString(s), Path::from(s)) or by another object (Process comes from ProcessBuilder.start() and has no constructor at all). Whichever way you obtain one, each satisfies the Closeable contract, so you pair the open with defer close() and the handle is released on every exit path (the compiler enforces the close with E0511):

    let f : File = new File("data.txt");
    defer f.close();

    Releasing a handle can itself fail, so close() carries ! IOException in its signature. A real program therefore either declares the set onward (fn main() : i32 ! IOException) or wraps the work in try { … } catch e : IOException { … } — the same no-silent-escape rule every fallible call obeys, see Error handling.

Ordinary data classes (ArrayList<T>, HashMap<K,V>, StringBuilder, LocalDateTime) are also built with new, but hold no OS handle, so they need no close(). A string handed to a collection is moved into it — see Collections for the s + "" copy idiom when the same value must live in two containers.

Recognising the shape at the call site

The spelling of a name tells you which shape you are dealing with before you open its reference page :

You see…It is a…Reach it with
mod::fn(args) after use std::mod;free functionbind the module, call through mod::
Class::method(args), nothing constructedfaçade (static-only class)call through ::; the prelude ones (Console, Random, …) need no use
new Class(args) then .method(…)instance classnew to build; if it holds a handle, pair with defer close()

The rule of thumb: capitalised name + :: = façade or constructor (a type), lowercase name + :: = module (a namespace of free functions). A façade groups related operations behind a type with no state to construct ; a module groups free functions behind an import. Both read as Name::thing, and the case of Name is what tells them apart.

Methods on primitives

Numeric and string operations are methods on the value, not statics on a utility class. There is nothing to import — the receiver’s type selects the method:

use std::numeric::math;
use std::lang::ParseException;

fn main() : i32 {
    let x : f64 = 2.5;
    println(x.sqrt());              // 1.5811388300841898
    println(x.floor());             // 2
    println((-x).abs().max(1.0));   // 2.5
    println(x.floorToInt());        // 2
    println(x.isFinite());          // 1

    println((255).toHex());         // ff
    println((-7).abs());            // 7
    println((3).min(9));            // 3

    println(math::pi());            // 3.141592653589793
    println(math::hypot(3.0, 4.0)); // 5

    try {
        println("42".toI32() + 1);  // 43
    } catch e : ParseException {
        eprintln("not a number");
    }
    return 0;
}

The surface, by receiver type:

ReceiverMethods
f64sqrt pow floor ceil trunc round abs min max sign cbrt exp ln log2 log10 sin cos tan asin acos atan sinh cosh tanh isNaN isFinite isInfinite truncToInt floorToInt ceilToInt
i32 / i64abs absChecked min max toHex toBin (toOct is i32-only)
stringtoI32 toI64 toF64 toBool — each raises ParseException. These are only the numeric conversions ; the full string surface (about 30 methods: charAt, substring, trim, replace, padStart, …) is on the std/text page

Every type also answers toString().

What is not a method lives in std::numeric::math as a free function, because it takes no natural receiver: the constants math::pi(), math::e(), math::tau(); the two-argument math::atan2(y, x) and math::hypot(x, y); and the type bounds math::i32Min(), math::i32Max(), math::i64Min(), math::i64Max(), math::f64PositiveInfinity(), math::f64NegativeInfinity(), math::f64NotANumber().

Two traps with a literal receiver.

A numeric literal needs parentheses before a method — (1.0).sqrt(), not 1.0.sqrt(), which lexes as a malformed number.

An untyped integer literal receiver is i32, and the result stays i32 even when the argument is wider: (1).max(bigI64) truncates. Type the receiver when you mean 64 bits — (1 as i64).max(bigI64).

Short module paths

Nested modules also answer to a flat short spelling — both forms name the same module, pick whichever reads better:

ShortFull path
use std::math;use std::numeric::math;
use std::random;use std::numeric::random;
use std::process;use std::sys::process;
use std::env;use std::sys::env;
use std::sysinfo;use std::sys::sysinfo;
use std::cli;use std::sys::cli;
use std::log;use std::system::log;
use std::regex;use std::text::regex;
use std::boxing;use std::text::boxing;
use std::util;use std::core::util;
use std::path;use std::io::path;

Module map

ModulePurposeRecipe
std/coreTiny pure helpers (ascii, bits, bytes, checks, util)—
std/langThe exception class hierarchy (Exception, RuntimeException, IOException, …), auto-imported. (Console is a separate prelude class from the runtime, not this module.)Error handling
std/collectionsArrayList<T> (T = i32 / i64 / f64 / string) / HashSet<T> (T = i32 / i64 / string), HashMap<K,V> (K = i32 / i64 / string, V any monomorphisable type)Collections
std/numericmath constants & free functions, Random PRNGNumeric and time
std/textString utilities, JSON / CSV / URL / regex / UUID / hashingStrings and text
std/ioFile I/O, path manipulationFiles and I/O
std/netTCP sockets, HTTP client, DNS resolution—
std/timeTime, datetime, sleepNumeric and time
std/sysClocks, OS info, exit / sleep, process / env / sysinfo / CLI argsCLI tools and system
std/system/logLevelled logging (log::setLevel(LogLevel::INFO))CLI tools and system
std/concurrentLocks (ReentrantLock, ReadWriteLock), channels, atomicsConcurrency primitives
std/ffi/libclibc / libm bindings (libc_malloc, libc_free, sqrt, …)FFI

API reference

The auto-generated API reference lists every public class, trait, and free function in the stdlib with the exact signature lifted from the source. Each entry carries the parsed @param / @return / @throws tags exactly as written above the declaration.

The reference is regenerated on every documentation build, so it always matches the stdlib that shipped with your axle binary.

See also

stdlibstandard-librarymodules