Chapter 09 6 min read

A tour of the standard library

You've met the language. Now meet the library. Most real programs spend their time calling standard-library functions — parsing input, transforming collections, reading files, talking JSON. This chapter is a guided walk through the pieces you'll reach for most.

One rule up front: `string` and `Vector<T>` are built in (always available), and everything else needs an `import`. The import path mirrors the file: stdlib::hashmap is one module, stdlib::math another.

Strings

string methods need no import. The ones you'll use constantly:

let s: string = "  Hello, Glide  ";
println!(s.trim());                  // "Hello, Glide"

println!(s.trim().to_lower());       // "hello, glide"

println!(s.contains("Glide"));       // true

println!(s.replace("Glide", "World")); // "  Hello, World  "


let csv: string = "a,b,c";
let parts: *Vector<string> = csv.split(",");   // ["a", "b", "c"]

println!(parts.len());               // 3

Parsing numbers out of text comes in two flavours: a hard parse_int (returns 0 on garbage) and a safe try_parse_int (returns !i32 so you can handle the failure):

let n: i32 = "42".parse_int();           // 42

let r: !i32 = "oops".try_parse_int();    // err(...)

if r.ok { println!(r.val); } else { println!("not a number"); }

Vectors

Vector<T> is the workhorse collection — built in, growable, owns its contents. Beyond push / get / len, it carries a full functional toolkit:

let v: *Vector<i32> = Vector::new();
v.push_all!(1, 2, 3, 4, 5);

let doubled: *Vector<i32> = v.map(double);        // [2,4,6,8,10]

let evens:   *Vector<i32> = v.filter(is_even);    // [2,4]

let total:   i32          = v.fold(0, add);        // 15

println!(v.sum());                                 // 15 (Vector<i32> only)


fn double(x: i32) -> i32 { return x * 2; }
fn is_even(x: i32) -> bool { return x % 2 == 0; }
fn add(acc: i32, x: i32) -> i32 { return acc + x; }

map, filter, fold, take, skip, any, all, find, reverse, extend — they take plain function pointers (no closures yet). For a Vector<string>, .join(sep) glues it back into one string:

let words: *Vector<string> = Vector::new();
words.push_all!("rock", "the", "casbah");
println!(words.join(" "));     // "rock the casbah"

HashMaps

HashMap<V> is a string-keyed hash table; the value is any type V. Import it:

import stdlib::hashmap::*;

fn main() -> i32 {
    let scores: *HashMap<i32> = HashMap::new();
    scores.insert("alice", 30);
    scores.insert("bob", 25);

    if scores.contains("alice") {
        println!("alice:", scores.get("alice"));   // 30

    }

    for k in scores.keys() {
        println!(k, "=>", scores.get(k));
    }
    return 0;
}

insert, get, contains, remove, len, keys(), values(), and entries() (a *Vector<*Pair<string, V>>) cover almost everything. Keys are always string.

Math

stdlib::math has the numeric helpers that aren't operators:

import stdlib::math::*;

fn main() -> i32 {
    println!(ipow(2, 10));        // 1024  (integer power)

    println!(gcd(48, 36));        // 12

    println!(abs_int(-7));        // 7

    println!(max_int(3, 9));      // 9

    println!(min_f64(1.5, 2.5));  // 1.5

    return 0;
}

Time

stdlib::time gives you a Duration type and clock access:

import stdlib::time::*;

fn main() -> i32 {
    let d: Duration = Duration::from_secs(90);
    println!(d.to_string());      // "1m 30s"


    let sw: Stopwatch = Stopwatch::start();
    // ... do work ...

    let elapsed: Duration = sw.elapsed();
    println!("took", elapsed.to_string());
    return 0;
}

Duration::from_millis / from_secs / from_minutes / from_hours build durations; .add / .sub combine two durations. A Stopwatch is the clean way to measure elapsed time — Stopwatch::start() then sw.elapsed(). (Recall from the concurrency chapter that after(d) turns a Duration into a timeout channel.)

Files and the environment

stdlib::fs reads and writes files; stdlib::env reaches the process environment.

import stdlib::fs::*;
import stdlib::env::*;

fn main() -> i32 {
    if fs_exists("config.txt") {
        let text: string = fs_read("config.txt");
        let lines: *Vector<string> = fs_read_lines("config.txt");
        println!("config has", lines.len(), "lines");
    }
    fs_write("out.txt", "generated by glide\n");

    let home: string = env_get("HOME");       // "" if unset

    if env_has("DEBUG") { println!("debug mode"); }
    return 0;
}

The fs_* functions are deliberately flat: fs_read, fs_write, fs_exists, fs_read_lines, fs_list(dir, suffix), fs_mkdir_p, fs_remove_file, and friends. fs_read_or_default(path, fallback) is the one-liner for "read this, or use a default if it's missing."

JSON

stdlib::json parses and builds JSON through one type, *JsonValue. Reading is the common case:

import stdlib::json::*;

fn main() -> i32 {
    let doc: *JsonValue = JsonValue::parse("{\"name\":\"alice\",\"age\":30}");

    // typed getters return !T — handle the missing/wrong-type case:

    let name: !string = doc.get_string("name");
    if name.ok { println!("name:", name.val); }   // alice


    let age: !i32 = doc.get_int("age");
    if age.ok { println!("age:", age.val); }       // 30

    return 0;
}

Building JSON goes the other way — construct values and emit() them to text:

import stdlib::json::*;

fn main() -> i32 {
    let obj: *JsonValue = JsonValue::object();
    obj.obj_set("id", JsonValue::int(7));
    obj.obj_set("ok", JsonValue::bool(true));
    println!(obj.emit());          // {"id":7,"ok":true}

    return 0;
}

The rest of the library, chapter by chapter

This tour skimmed the surface. The chapters that follow are the deep reference — every public function, with worked examples — one chapter per module:

Networking culminates in Building an HTTP server; testing lives in Testing and benchmarks.

Recap

Where to next

You've seen the shape of the library. The next chapter — Prelude — starts the module-by-module reference with what's always in scope, and the chapters after it work through each stdlib:: module in turn.