Standard Library

Built-in types: String, Vec, HashMap, Iterator, IO.

Three-tier architecture

Hew's standard library is organized into three tiers, enabling use in environments ranging from bare-metal to full OS:

  • core — No allocation, no OS. Primitive types, Option<T>, Result<T, E>, Iterator trait, marker traits (Send, Frozen, Copy), and memory intrinsics. Works on bare metal.
  • alloc — Heap allocation, no OS. Vec<T>, string, Box<T>, Arc<T>, Rc<T>, HashMap<K, V>, HashSet<T>, and formatting. Works anywhere with a heap.
  • std — Full OS integration. File system (std::fs), networking (std::net), IO (std::io), environment (std::env), and process spawning (std::process).

string

string is an owned, heap-allocated, growable UTF-8 string. Internally it wraps a Vec<u8> that is guaranteed to contain valid UTF-8.

string
type string {
    // Internal: Vec<u8> guaranteed valid UTF-8
}
impl String {
    fn new() -> String;
    fn from(s: str) -> String;
    fn len(self) -> usize;
    fn push(self, c: char);
    fn push_str(self, s: str);
    fn as_str(self) -> str;
}

Vec<T>

Vec<T> is a growable, heap-allocated array. It manages a contiguous block of memory with a length and capacity.

Vec<T>
impl<T> Vec<T> {
    fn new() -> Vec<T>;
    fn with_capacity(cap: usize) -> Vec<T>;
    fn push(self, item: T);
    fn pop(self) -> Option<T>;
    fn len(self) -> usize;
    fn get(self, index: usize) -> Option<T>;
    fn truncate(self, len: usize);
    fn clone(self) -> Vec<T>;
    fn swap(self, a: usize, b: usize);
    fn sort(self) where T: Ord;
}
// Default: uses actor's current allocator
let items = Vec::new();
// Explicit: uses provided allocator
let temp = Vec::new_in(arena_allocator);

HashMap<K, V>

HashMap<K, V> is a hash table using Robin Hood hashing. Keys must implement Hash + Eq.

HashMap<K, V>
impl<K: Hash + Eq, V> HashMap<K, V> {
    fn new() -> HashMap<K, V>;
    fn insert(self, key: K, value: V) -> Option<V>;
    fn get(self, key: K) -> Option<V>;
    fn remove(self, key: K) -> Option<V>;
    fn contains_key(self, key: K) -> bool;
}

Iterator

The Iterator trait provides lazy, composable data transformation with combinators like map, filter, fold, and collect.

Iterator
trait Iterator {
    type Item;
    fn next(self) -> Option<Self::Item>;
    // Provided combinators
    fn map<B>(self, f: fn(Self::Item) -> B) -> Map<Self, B>;
    fn filter(self, pred: fn(Self::Item) -> bool) -> Filter<Self>;
    fn collect<C: FromIterator<Self::Item>>(self) -> C;
    fn fold<B>(self, init: B, f: fn(B, Self::Item) -> B) -> B;
}
trait IntoIterator {
    type Item;
    type IntoIter: Iterator<Item = Self::Item>;
    fn into_iter(self) -> Self::IntoIter;
}

IO

The IO system is built on Read and Write traits. All IO operations return Result — there is no implicit blocking.

IO traits
trait Read {
    fn read(self, buf: [u8]) -> Result<usize, IoError>;
    fn read_exact(self, buf: [u8]) -> Result<(), IoError>;
    fn read_to_end(self, buf: Vec<u8>) -> Result<usize, IoError>;
    fn read_to_string(self, buf: String) -> Result<usize, IoError>;
}
trait Write {
    fn write(self, buf: [u8]) -> Result<usize, IoError>;
    fn flush(self) -> Result<(), IoError>;
    fn write_all(self, buf: [u8]) -> Result<(), IoError>;
}
trait BufRead: Read {
    fn fill_buf(self) -> Result<[u8], IoError>;
    fn consume(self, amt: usize);
    fn read_line(self, buf: String) -> Result<usize, IoError>;
}

The File type wraps a file descriptor with safe open/close semantics using Drop for deterministic resource cleanup:

File
type File { fd: i32; }
impl File {
    pub fn open(path: String) -> Result<File, IoError> {
        let c_path = path.to_c_string();
        let fd = unsafe { open(c_path.as_ptr(), O_RDONLY) };
        if fd < 0 {
            Err(IoError::from_errno())
        } else {
            Ok(File { fd })
        }
    }
}
impl Drop for File {
    fn drop(f: File) {
        unsafe { close(f.fd); }
    }
}
// File I/O convenience functions
fn read_file(path: String) -> Result<String, IoError>;
fn write_file(path: String, content: String) -> Result<(), IoError>;

File I/O

The std::fs module provides convenience functions for reading, writing, and deleting files. The std::stream module adds streaming file access for line-by-line processing without loading the entire file into memory.

File I/O
import std::fs;
import std::stream;
import std::os;
fn main() {
    let tmp = os.temp_dir();
    let path = tmp + "/example.txt";
    // Write and read a file
    fs.write(path, "apple\nbanana\ncherry\n");
    let content = fs.read(path);
    let size = fs.size(path);
    println(f"Size: {size} bytes");
    // Stream: read file line by line
    let raw = match stream.from_file(path) {
        Ok(s) => s,
        Err(e) => { panic(e); },
    };
    let lines = raw.lines();
    for await line in lines {
        println(line);
    }
    // Stream: write to a file
    let out_path = tmp + "/output.txt";
    let sink = match stream.to_file(out_path) {
        Ok(s) => s,
        Err(e) => { panic(e); },
    };
    sink.write("hello ");
    sink.write("from ");
    sink.write("hew");
    sink.flush();
    sink.close();
    // Cleanup
    fs.delete(path);
    fs.delete(out_path);
}

CLI arguments

The std::os module provides access to command-line arguments. Use os.args_count() for the argument count and os.args(i) to get individual arguments by index. String methods like starts_with, find, and slice handle parsing.

CLI argument parsing
import std::os;
fn main() {
    let argc = os.args_count();
    if argc <= 1 {
        println("Usage: myapp [OPTIONS] [FILES...]");
        return;
    }
    var i = 1;
    while i < argc {
        let arg = os.args(i);
        if arg.starts_with("--") {
            let eq_pos = arg.find("=");
            if eq_pos >= 0 {
                let key = arg.slice(2, eq_pos);
                let val = arg.slice(eq_pos + 1, arg.len());
                println(f"{key} = {val}");
            } else {
                println(f"flag: {arg}");
            }
        } else {
            println(f"file: {arg}");
        }
        i += 1;
    }
}

Stream channels

std::stream.channel(capacity) creates a bounded, typed channel for passing data between producers and consumers. The producer writes to the sink; the consumer reads with for await. Close the sink to signal completion.

Stream channels
import std::stream;
fn main() {
    let (sink, input) = stream.channel(8);
    // Producer writes to sink
    sink.write("alpha");
    sink.write("beta");
    sink.write("gamma");
    sink.write("delta");
    sink.close();
    // Consumer reads with for-await
    for await item in input {
        println(item);
    }
    input.close();
}

Allocators

Hew provides an explicit allocator interface for fine-grained memory control. Collections can be parameterized over allocators.

  • GlobalAllocator — Default system allocator
  • ArenaAllocator — Bump allocation with O(1) bulk deallocation
  • PoolAllocator — Fixed-size object pools for uniform allocations
Allocator trait
trait Allocator {
    fn alloc(self, size: usize, align: usize) -> Result<*var u8, AllocError>;
    fn dealloc(self, ptr: *var u8, size: usize, align: usize);
}