Capítulo 09 6 min de leitura

Um passeio pela biblioteca padrão

Você já conheceu a linguagem. Agora conheça a biblioteca. A maior parte dos programas reais passa o tempo chamando funções da biblioteca padrão — interpretando entradas, transformando coleções, lendo arquivos, conversando com JSON. Este capítulo é um passeio guiado pelas peças que você mais vai usar.

Uma regra antes de começar: `string` e `Vector<T>` são embutidos (sempre disponíveis), e todo o resto precisa de um `import`. O caminho de importação espelha o arquivo: stdlib::hashmap é um módulo, stdlib::math é outro.

Strings

Os métodos de string não precisam de importação. Os que você vai usar o tempo todo:

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

Converter texto em números vem em dois sabores: um parse_int direto (retorna 0 para entradas inválidas) e um try_parse_int seguro (retorna !i32 para que você trate a falha):

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"); }

Vetores

Vector<T> é a coleção principal — embutida, dinâmica, possui seus conteúdos. Além de push / get / len, ela traz um kit funcional completo:

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 (apenas Vector<i32>)

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 — todos recebem ponteiros de função simples (sem closures por enquanto). Para um Vector<string>, .join(sep) une tudo de volta em uma única string:

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

HashMaps

HashMap<V> é uma tabela hash com chaves do tipo string; o valor pode ser qualquer tipo V. Importe-a assim:

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() e entries() (um *Vector<*Pair<string, V>>) cobrem quase tudo. As chaves são sempre string.

Math

stdlib::math oferece os auxiliares numéricos que não são operadores:

import stdlib::math::*;

fn main() -> i32 {
    println!(ipow(2, 10));        // 1024  (potência inteira)
    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 fornece um tipo Duration e acesso ao relógio:

import stdlib::time::*;

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

    let sw: Stopwatch = Stopwatch::start();
    // ... faz o trabalho ...
    let elapsed: Duration = sw.elapsed();
    println!("took", elapsed.to_string());
    return 0;
}

Duration::from_millis / from_secs / from_minutes / from_hours constroem durações; .add / .sub combinam duas durações. Um Stopwatch é a forma limpa de medir o tempo decorrido — Stopwatch::start() e depois sw.elapsed(). (Lembre do capítulo de concorrência que after(d) transforma uma Duration em um canal de timeout.)

Arquivos e o ambiente

stdlib::fs lê e escreve arquivos; stdlib::env acessa o ambiente do processo.

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");       // "" se não definido
    if env_has("DEBUG") { println!("debug mode"); }
    return 0;
}

As funções fs_* são deliberadamente simples e planas: fs_read, fs_write, fs_exists, fs_read_lines, fs_list(dir, suffix), fs_mkdir_p, fs_remove_file e outras. fs_read_or_default(path, fallback) é a forma de uma linha para "lê isso, ou usa um valor padrão se estiver faltando."

JSON

stdlib::json analisa e constrói JSON por meio de um único tipo, *JsonValue. A leitura é o caso mais comum:

import stdlib::json::*;

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

    // getters tipados retornam !T — trate o caso de ausência ou tipo errado:
    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;
}

Construir JSON funciona ao contrário — monte os valores e chame emit() para obter o texto:

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;
}

O restante da biblioteca, capítulo por capítulo

Este passeio arranhamos a superfície. Os capítulos que se seguem são a referência aprofundada — cada função pública, com exemplos elaborados — um capítulo por módulo:

O networking culmina em Construindo um servidor HTTP; testes ficam em Testes e benchmarks.

Recap

O que vem a seguir

Você já viu o formato da biblioteca. O próximo capítulo — Prelude — inicia a referência módulo a módulo com o que está sempre em escopo, e os capítulos seguintes percorrem cada módulo stdlib:: por vez.