The Standard Library
std is written entirely in Cryo and ships with the compiler as a static library plus its sources. Nothing in it is privileged: the same type struct, type trait, and implement declarations available to your code build Array<T>, HashMap<K, V>, and the HTTP server. When a signature here looks like something you could have written, that is because it is.
The library is layered. core sits at the bottom and neither allocates nor performs I/O — it is the vocabulary the language itself is defined against. alloc introduces the heap, collections builds growable containers on top of it, and everything above that is domain-specific: files, sockets, threads, formatting, time.
How to read these pages
The tree mirrors the compiler repository's stdlib/ directory: one page per module (collections), and under it one page per source file (collections/array), so a page's URL is its import path. A module page opens with the module's own manifest — the public module lines from its _module.cryo — and lists its files; a file page opens each type and trait with its declaration as it appears in the source, minus method bodies:
type struct Pair<A, B> {
first: A;
second: B;
static new(first: A, second: B) -> Pair<A, B>;
}
Fields are listed because layout matters in a systems language; methods are listed as signatures. Private members and helpers the sources mark as internal are omitted. Where a type implements traits, a Trait implementations section lists each implement head, naming the module alongside when the impl lives somewhere other than the type's own — Display and Debug for the core types live in fmt, for instance, because core cannot import upward.
The declarations are not typed in by hand. A sync from the compiler repository extracts every signature in stdlib/ into an index the site builds from, so what a page shows is what the compiler ships — and a page whose declaration has disappeared fails the build rather than going quietly stale. The prose around each block is the part the signatures cannot say: what a method's contract is, which bounds are load-bearing, and why a design is the shape it is.
The prelude
A small set of names is imported into every source file automatically. The set is deliberately short — each entry competes for your attention — and most of it is there because the language desugars to it.
| Module | What it brings in |
|---|---|
core | panic(message, loc) -> never, plus assert, unreachable, and todo |
core::option | Option<T> and its methods |
core::result | Result<T, E> and its methods |
core::primitives | Methods on the built-in types (i32::max_value, char::is_digit, ...) |
core::varargs | VaArgs, the compiler-assigned type of a function's args... bucket |
collections::array | Array<T>, because [T] is a spelling of it |
core::slice | Slice<T>, which backs for (x in arr) over a fixed-size array |
core::ops | Range and RangeInclusive, because a..b desugars to Range::new; the operator traits |
core::iter | Iterator, the trait for-in drives its scrutinee through |
core::drop, core::marker, core::cmp, core::clone | Drop, Copy, Send, Sync, Eq, Ord, Clone — the traits the compiler reasons about by identity, so an implement trait Drop for X head resolves without an import |
alloc::box | Box<T> |
alloc::rc | Rc<T> |
The f-string runtime in fmt::interp is the one addition outside this list: the compiler injects import std::fmt::interp; into any module that contains an f"...", so the desugar resolves without the import appearing in every file.
Everything else is an explicit import. Most notably the print / println / eprint / eprintln family lives in fmt and is not in the prelude:
namespace app;
import std::fmt;
function main() -> i32 {
println(f"hello, {name}");
return 0;
}
Importing
Standard library paths are rooted at std, and you import the module that owns the item you want:
import std::collections::array; // the module
import std::collections::{ str, string }; // two modules from one parent
import std::net::http::status as http_status;
import std::fmt::display::*; // everything public in the module
Each module page below records the exact import path for the types it documents. Importing a parent aggregator (import std::collections;) brings in whatever that directory's _module.cryo marks public. See Modules and Imports for the full rules.
Module directory
public module prelude;
public module core;
public module alloc;
public module collections;
public module io;
public module fmt;
public module json;
public module encoding;
public module fs;
public module ffi;
public module sys;
public module env;
public module math;
public module time;
public module random;
public module net;
public module process;
public module thread;
public module sync;
public module future;
public module test;
| Module | What it covers |
|---|---|
core | Option, Result, Slice, NonNull, Range, Ordering, and the trait vocabulary the language is defined against |
alloc | Layout, the Allocator trait, Box, Rc, Arc, Arena, Pool |
collections | Array, Str, String, HashMap, HashSet, Pair |
fmt | Display, Debug, Formatter, the print family, f-string format specs, heap-free number writers |
json | RFC 8259 value model, parser, and serializer |
encoding | Base64 and SHA-1 |
io | Read / Write / Seek, the async traits, the standard streams, buffered adapters, Cursor, IoError |
fs | Path, PathBuf, File, directories, metadata |
env | Arguments, environment variables, working directory, process exit |
process | Command, Child, Stdio, ExitStatus, signals, the platform shell |
time | Duration, Instant, SystemTime, DateTime, sleep |
ffi | The C ABI boundary: libc and openssl |
sys | Raw syscalls, and the Win32 / NT surfaces |
math | libm wrappers, width-generic where it makes sense |
random | Rng (xoshiro256**), SecureRng, and distributions |
sync | Atomic, Mutex, RwLock, CondVar, Once, Barrier, mpsc channels |
thread | spawn, JoinHandle, Builder, scoped threads, thread-local storage |
future | Future, Poll, Context, Waker, the executor, reactor, blocking pool, and combinators |
net | IP addressing, DNS, async TCP, UDP, TLS, and the HTTP/1.1, HTTP/2, and WebSocket layers |
test | The built-in unit-test framework |
Conventions
The standard library follows a handful of rules consistently. User code is encouraged to mirror them, and knowing them up front means you can predict most signatures before reading them.
Result for expected failure, panic for broken invariants. A function returns Result<T, E> whenever failure is part of its contract — a file that might not exist, a parse that might not succeed. It panics only when the contract cannot be preserved at all, such as an out-of-bounds index on a method whose contract excludes it. Fallible constructors are usually spelled try_* next to a panicking sibling.
No NUL-terminated strings outside ffi. Every other module works in length-typed Str and String values. Translation to and from C strings happens at the boundary, in ffi.
Explicit resource management. Every owning type exposes drop(mut &this), and its documentation says who is responsible for calling it. In practice the compiler synthesises drops at scope exit — see Ownership, Copy, and Drop — and a manual .drop() remains available for early release.
Allocator-generic containers. Array<T, A>, HashMap<K, V, A>, String<A>, Box<T, A>, Rc<T, A>, and PathBuf<A> all take the allocator as a trailing type parameter defaulting to GlobalAlloc. The *_in constructors plug in an arena, a pool, or your own strategy without changing any other code.
Everything that waits is a future. Sockets, TLS, child processes, and timers expose no blocking calls; each operation that can wait is a Future driven by an Executor, and an operation owns what it works on — the handle and the buffer — handing both back on completion.
No magic numbers. Every numeric constant in the library is named. A bare literal in a signature is a bug report waiting to happen.
Reading the sources
Each page here documents the surface, but the library is short and readable, and its doc comments are the ground truth. The manifest at stdlib/lib.cryo lists every module, and each directory's _module.cryo describes what its files contain.
type struct Name { ... }
type enum Name { ... }
type trait Name { ... }
type union Name { ... }Begins a type declaration; the keyword that follows says which kind. On its own, type Name = Existing; declares an alias.
type struct Name {
field: Type;
method(&this) -> Ret { ... }
}A value type with named fields and optional methods. Structs live on the stack and are passed by value. Fields are public by default and may carry = default values; methods take their receiver as &this, mut &this, or this.
type struct Pair<A, B>type parameter Atype parameter BPair.first: Atype parameter APair.second: Btype parameter Bstatic name(param: Type) -> Ret { ... }A method that belongs to the type rather than an instance, called as Type::name(..). For structs, static new(..) returning a struct literal is the idiomatic constructor.
static Pair::new(first: A, second: B) -> Pair<A, B>(parameter) first: A(parameter) second: Btype struct Pair<A, B>namespace app::module;Every source file opens with one: its position in the module hierarchy. The compiler uses it to resolve imports and to mangle symbol names, so two parse functions in different modules never collide at link time.
import a::b;
import a::b::{ X, Y };
import a::b as c;
import a::b::*;Brings a module, or selected items from it, into scope. One path per declaration: use the brace form for several items, as to alias, and * for everything public (prefer the brace form; wildcards invite name collisions).
function name<T>(param: Type) -> Ret { ... }Declares a function. Parameters are written name: Type and are never inferred; the return type follows -> and defaults to void. Declaration order does not matter: every signature is collected before any body is checked, so functions may call each other freely and recurse.
function main() -> i32return value;Leaves the current function with the given value. A function whose return type is not void must return one; a bare return; is for void. In an async function it completes the future with the value.
namespace std::collections::array;Array<T, A>: growable, heap-backed, contiguous sequence.
module pageprimitive stringNUL-terminated raw string (u8*), matching the C ABI. Length-typed text is Str / String.
public function name() { ... }
public:Visible to any module that imports this one - the default for top-level items and for struct fields, so it is mostly written to be explicit. public: opens a visibility block that groups the members after it.
public module collections::array;In a directory's _module.cryo aggregator, lists a submodule and whether it is public. Importing the directory's namespace sees only the modules declared public here.
namespace std::prelude;Prelude: auto-imported into every Cryo source file.
namespace std::future;std::future: stackless async foundations — Future, Poll, Context, Waker, and the drivers that poll them.
type trait Name : Base {
method(&this, other: &This) -> Ret;
}Names a set of methods a type may implement. Inside the body This is the implementing type; methods may carry default bodies, and a trait may require a base trait (type trait Ord : Eq). Generic code asks for a trait with where T: Name.
implement struct Name { ... }
implement trait Trait for Type { ... }
implement u64 { ... }Adds methods to a type outside its declaration. implement struct T (or enum, or a bare primitive) opens an inherent block; implement trait Tr for T supplies a trait's methods for one type. Resolution is at compile time - no dynamic dispatch.
type struct Array<T, A = GlobalAlloc> where A: Allocator {
ptr: T*;
length: i64;
capacity: i64;
alloc: A;
}Array<T, A>: growable, heap-backed, contiguous sequence.
std::collections::arraytype struct HashMap<K, V, A = GlobalAlloc> where A: Allocator {
buckets: Entry<K, V>**;
bucket_count: u64;
length: u64;
alloc: A;
seed: u64;
}HashMap<K, V, A>: key-value map with amortized O(1) operations.
std::collections::hashmaptype trait Display {
fmt<W>(&this, f: mut &Formatter<W>) -> Result<(), FmtError>
where W: FmtWrite;
}Display / Debug / Formatter.
std::fmt::displaytype trait Debug {
fmt<W>(&this, f: mut &Formatter<W>) -> Result<(), FmtError>
where W: FmtWrite;
}function panic(message: string, loc: intrinsics::Location) -> neverPanic with message, reporting the place loc: intrinsics::location() written at the call, or in a ![caller_location] function its caller's. The helpers below do that automatically.
primitive neverThe type of an expression that does not return. Only valid as a return type, written by functions that always panic, trap, or diverge. 0 bytes.
primitive typefunction assert(condition: boolean, message: string) -> voidPanic if condition is false. Always checked.
function unreachable() -> neverMark a code path the programmer believes cannot be reached. If it ever is, the program aborts with a clear message rather than executing wrong code.
std::corefunction todo() -> neverStub a function you'll implement later. Reading better than inlining panic("not yet implemented", ...) with the place is its whole job.
static i32::max_value() -> i32char::is_digit(&this) -> booleannamespace std::core::varargs;VaArgs: a safe, typed wrapper over a C-ABI variadic argument list.
module pagetype struct VaArgs {
ptr: void*;
}Single-pointer wrapper over the active va_list. ptr is the raw va_list pointer - the args identifier the compiler binds in a variadic body.
function name(count: i32, args...)A variadic parameter: the trailing bucket receives any number of further arguments with C's calling convention, so the function stays ABI-compatible with the printf family. Wrap the bucket in VaArgs to read typed values from it.
for (mut i: i32 = 0; i < n; i++) { ... }
for (item in iterable) { ... }Two forms. The C-style for (init; condition; step) scopes its loop variable to the body. for (x in expr) iterates anything with next() -> Option<T>: an iterator, a range such as 0..n, an Array or Slice (their iter() is inserted for you), or a fixed-size array.
xfor (item in iterable) { ... }Separates the loop variable from the sequence in a for loop. The sequence is evaluated once; the loop lowers to loop { match (it.next()) { Some(x) => .., None => break } }.
type struct Range<T> {
start: T;
end: T;
}type struct RangeInclusive<T> {
start: T;
end: T;
exhausted: boolean;
}start..endA half-open range - start up to but excluding end - and sugar for Range::new(start, end): an iterator usable in for, and an ordinary value anywhere else. It binds looser than arithmetic, so a..b + 1 is a..(b + 1).
static Range::new(start: T, end: T) -> Range<T>function drop<T>(_v: T) -> voidExplicitly drop a value of type T. This is a no-op for types that do not implement a destructor. The caller is responsible for ensuring that any resources held by _v are properly released if necessary.
type trait Copy {}Types whose values can be duplicated by a bitwise copy. Integers, floats, pointers, and pure-POD structs qualify; anything that owns resources (heap storage, file handles, refcount) does not.
std::core::markertype trait Send {}Types that are safe to transfer between threads. An owning value of a Send type may cross a thread boundary without violating memory safety.
type trait Sync {}Types that are safe to share between threads by shared reference. T: Sync if &T: Send.
type trait Eq {
equals(&this, other: &This) -> boolean;
}Types that support value equality. equals(a, b) must be reflexive, symmetric, and transitive.
type trait Ord : Eq {
compare(&this, other: &This) -> Ordering;
}Types that have a total order: every pair is Less, Equal, or Greater. compare must be consistent with equals: if a.equals(b) then a.compare(b) == Equal.
implement trait Trait for Type { ... }Introduces a trait implementation: the block supplies, for one concrete type, the method bodies the trait requires.
language referenceimplement trait Trait for Type { ... }Names the type a trait implementation is for. The block that follows supplies the bodies the trait requires.
language referencenamespace std::alloc::rc;Rc<T, A>: single-threaded reference counting, with weak references.
module pagetype struct Slice<T> {
ptr: T*;
length: u64;
}Slice<T>: a borrowed view into a contiguous sequence.
std::core::slicetype struct NonNull<T> {
ptr: T*;
}type enum Ordering {
Less;
Equal;
Greater;
}type struct Layout {
size: u64;
alignment: u64;
}Layout: size + alignment of a memory block.
std::alloc::layouttype trait Allocator {
allocate(mut &this, layout: Layout) -> Result<NonNull<u8>, AllocError>;
deallocate(mut &this, ptr: NonNull<u8>, layout: Layout) -> void;
reallocate(mut &this, ptr: NonNull<u8>, old_layout: Layout, new_layout: Layout) -> Result<NonNull<u8>, AllocError>;
}Allocator: the trait every heap strategy implements.
std::alloc::allocatortype struct Box<T, A = GlobalAlloc> where A: Allocator {
ptr: T*;
alloc: A;
}Box<T, A>: single-owner heap allocation.
std::alloc::boxtype struct Rc<T, A = GlobalAlloc> where A: Allocator {
inner: RcInner<T, A>*;
}Rc<T, A>: single-threaded reference counting, with weak references.
std::alloc::rctype struct Arc<T, A = GlobalAlloc> where A: Allocator {
inner: ArcInner<T, A>*;
}Arc<T, A>: atomic reference counting, with weak references.
std::alloc::arctype struct Arena {
head: Chunk*;
current: Chunk*;
chunk_size: u64;
}Arena: bump allocator with bulk release.
std::alloc::arenatype struct Pool {
slot: Layout;
slots_per_block: u64;
free_list: FreeNode*;
blocks: Block*;
}Pool: O(1) allocator for uniformly-sized slots.
std::alloc::pooltype struct Str {
bytes: u8*;
length: u64;
}Str: a borrowed UTF-8 string slice.
std::collections::strtype struct String<A = GlobalAlloc> {
buffer: RawBuffer<u8, A>;
length: u64;
}String: owned, heap-allocated, length-typed UTF-8.
std::collections::stringtype struct HashSet<T, A = GlobalAlloc> {
map: HashMap<T, (), A>;
}HashSet<T, A>: unordered collection of unique values.
std::collections::hashsettype struct Pair<A, B>type struct Formatter<W> {
writer: W*;
}type trait Read {
read(mut &this, buffer: u8*, length: u64) -> Result<u64, IoError>;
read_all(mut &this, buffer: u8*, length: u64) -> Result<u64, IoError>;
read_byte(mut &this) -> Result<Option<u8>, IoError>;
read_char(mut &this) -> Result<Option<u32>, IoError>;
read_until(mut &this, delimiter: u8, out: mut &Array<u8>) -> Result<u64, IoError>;
read_exact(mut &this, buffer: u8*, length: u64) -> Result<(), IoError>;
read_to_end(mut &this, out: mut &Array<u8>) -> Result<u64, IoError>;
read_to_string(mut &this, out: mut &String) -> Result<u64, IoError>;
read_line(mut &this, out: mut &String) -> Result<u64, IoError>;
}type trait Write {
write_some(mut &this, bytes: Slice<u8>) -> Result<u64, IoError>;
flush(mut &this) -> Result<(), IoError>;
write<T>(mut &this, data: T) -> Result<(), IoError>;
}type trait Seek {
seek(mut &this, from: SeekFrom) -> Result<u64, IoError>;
stream_position(mut &this) -> Result<u64, IoError>;
rewind(mut &this) -> Result<(), IoError>;
}A byte stream whose read/write cursor can be repositioned. Absolute positions are u64 offsets from the start; every seek returns the resulting absolute position.
type struct Cursor {
buffer: Array<u8>;
pos: u64;
}Cursor is also declared in std::net::http2::hpack
An in-memory Read + Write + Seek byte buffer. Owns its Array<u8>; drop releases it (or move it out with into_inner first).
type struct IoError {
kind: IoErrorKind;
os_code: i32;
}type struct Path {
inner: Str;
}Path / PathBuf: filesystem paths as length-typed UTF-8.
std::fs::pathtype struct PathBuf<A = GlobalAlloc> {
inner: String<A>;
}type struct Command {
program: String;
args: Array<String>;
envs: Array<EnvEntry>;
clear_env_flag: boolean;
cwd: Option<String>;
stdin_cfg: Stdio;
stdout_cfg: Stdio;
stderr_cfg: Stdio;
}Command: builder for spawning a subprocess.
std::process::commandtype struct Child {
stdin: Option<ChildStdin>;
stdout: Option<ChildStdout>;
stderr: Option<ChildStderr>;
}Child: a running subprocess.
std::process::childtype enum Stdio {
Inherit;
Null;
Piped;
Fd(i32);
}How a child's stdin / stdout / stderr is wired up.
std::process::commandtype struct ExitStatus {
code: i32;
signal: i32;
signaled: boolean;
}Result of a child's termination. Either it exited normally with a code, or it was killed by a signal. POSIX encodes both into a single wstatus word; ExitStatus is the decoded form.
type struct Duration {
secs: u64;
nanos: u32;
}A length of time: whole secs plus a sub-second nanos remainder (0 <= nanos < 1_000_000_000). POD; copy-by-value, no drop.
type struct Instant {
secs: i64;
nanos: i64;
}A measurement of the monotonic clock. Opaque: the fields are the raw timespec, meaningful only relative to another Instant. POD.
type struct SystemTime {
secs: i64;
nanos: i64;
}A measurement of the wall clock (CLOCK_REALTIME) as a raw timespec. duration_since_epoch() yields a Unix timestamp. Subject to NTP steps and manual changes; do not use for elapsed-time measurement. POD.
type struct DateTime {
year: i64;
month: u32;
day: u32;
hour: u32;
minute: u32;
second: u32;
}A UTC civil calendar reading. All fields are UTC and already normalized: month in 1..=12, day in 1..=31, hour in 0..=23, minute/second in 0..=59. year is the proleptic Gregorian year and may be negative for timestamps far before the Common Era. POD.
type struct Rng {
s0: u64;
s1: u64;
s2: u64;
s3: u64;
}A fast non-cryptographic PRNG (xoshiro256**). The 256-bit state is the four words s0..s3.
type struct SecureRng {}Cryptographically secure source backed by the kernel CSPRNG. Slower than Rng; use it only where unpredictability matters.
type struct Atomic<T> {
value: T;
}A single atomic cell of type T. See the module docs for the supported T and the compile-time dispatch model.
type struct Mutex<T, A = GlobalAlloc> where A: Allocator {
inner: MutexInner<T, A>*;
}Mutex<T>: mutual-exclusion lock around a value.
std::sync::mutextype struct RwLock<T, A = GlobalAlloc> where A: Allocator {
inner: RwLockInner<T, A>*;
}RwLock<T>: read/write reader-writer lock around a value.
std::sync::rwlocktype struct CondVar {
inner: CondVarInner*;
}CondVar: condition variable for blocking until a predicate holds.
std::sync::condvartype struct Once {
inner: OnceInner*;
}Once: one-shot initialization synchronization.
std::sync::oncetype struct JoinHandle<O> {
shared: TaskShared<O>*;
exec: ExecInner*;
}JoinHandle is also declared in std::thread
A handle to a spawned task, yielding its Output via join(). Dropping a handle without join/detach detaches (the task runs on, its result discarded). abort() requests cancellation.
type struct Builder {
stack_bytes: u64;
name_buf: [u8;
16];
name_len: u64;
}A configurable thread spawn: set a stack size and/or an OS-visible thread name, then spawn (panics on failure) or try_spawn (returns a Result). Configuration is applied through a pthread_attr_t; the name is set with pthread_setname_np once the thread is running.
type trait Future {
type Output;
poll(mut &this, cx: Context*) -> Poll<This::Output>;
}An asynchronous computation. poll drives it one step: Ready(v) when the value is available, Pending when it is not yet (the future has arranged, via cx's waker, to be polled again later).
type enum Poll<T> {
Ready(T);
Pending;
}The result of polling a Future: either the final value, or a signal that the value is not ready yet and the task should be re-polled after its Waker fires.
type struct Context {
waker: Waker;
}The context threaded through a poll call. Holds the Waker a future registers when it returns Pending. Passed by pointer down the poll tree, matching the stdlib convention of moving aggregates by pointer.
type struct Waker {
data: u8*;
}A handle a stalled future stores so it can be re-scheduled: wake_fn(data) tells the executor "poll my task again". This is the manual-vtable shape used throughout the runtime — non-capturing function pointers plus an opaque data pointer identifying the task.
mut &this
mut &TypeAn exclusive, mutating borrow. As a receiver, mut &this lets the method modify the fields - and callers can see that from the signature alone. As a parameter type, mut &T lets the callee mutate through the reference while the caller keeps ownership.
&this
mut &this
thisThe receiver of a method. Its first parameter declares how the method takes it: &this borrows read-only, mut &this borrows exclusively, and this / mut this move the value in and consume it. In the body, this.field reaches the members.
type struct GlobalAlloc {}Zero-sized handle to the process-wide allocator. Construct with GlobalAlloc::new(); all instances dispatch through the same underlying backend.