Standard library menu

Conventions

Every module in the standard library follows the same few rules. Learn them once and every module feels familiar.

By the end of this page you will

  • Know which modules are always there and which need an import.
  • Tell, from a function's name and signature, how it reports a problem.
  • Close files and sockets safely with defer.
  • Know what the standard library promises to keep working in Turf 2.

What this page is for

A standard library is a box of ready-made tools that comes with the language. You could write a function that finds the biggest number in a list yourself, but someone already did, tested it, and put it in the box.

This page is the instruction sheet for the whole box. It does not describe any one tool. It describes the rules every tool follows, so that after you have used two or three modules, you can guess how the next one behaves.

This page says how the standard library is shaped: how it names things, how its functions report failure, how it handles resources, what is public, and what a 2.x program can rely on. The other pages in this folder document one module each.

Modules

Module For Import Page
int, double, bool, char helpers on the primitive types: n.gcd(m), x.round(), c.is_digit() none (prelude) Core: numbers, text and arrays
strings string search, slicing, trimming, splitting, parsing, StringBuilder none (prelude) Core: numbers, text and arrays
array sum, max, min, contains, index_of over int[], double[], string[] none (prelude) Core: numbers, text and arrays
io printing, reading input, reading and writing files, exists none (prelude) I/O and networking
math abs, min, max, clamp, power, libm functions, divide, constants none (prelude) Math and the operating system
sys panic, exit, abort none (prelude) I/O and networking
list List<T>, a growable array import list::List; Collections
stack Stack<T>, last in first out import stack::Stack; Collections
queue Queue<T>, first in first out import queue::Queue; Collections
map Map<K, V>, a hash map import map::Map; Collections
set Set<T>, a hash set import set::Set; Collections
hashing hash(x), the hash Map and Set use import hashing; Collections
net TCP client and server sockets import net; I/O and networking
fs path strings and directories import fs; Files and directories
os environment variables and command-line arguments import os; Math and the operating system
time the clock, elapsed time, sleeping import time; Time
random pseudo-random numbers import random; Random numbers
mem manual-heap allocation and raw memory import mem; Memory
json parse and build JSON import json; JSON
http a small HTTP/1.1 server and request/response types import http; HTTP
test assert, assert_eq, fail import test; Testing

A module outside the prelude must be imported in each file that writes its qualifier: import list; lets a file write list::List<int>, while import list::List; lets it write List<int> but not list::List. A file that needs both writes both. See Functions and modules.

The prelude

Before you write a line, Turf has already loaded nine modules into every file. That set is the prelude, and it is why printline("hi") works with no import. The nine split into two groups, by how you reach them.

  • Bare namesio, math, sys

    Call them by their plain name: printline("hi"), abs(-3), max(a, b), sqrt(2.0), exit(1), panic("message").

  • Dot or qualifierint, double, bool, char, strings, array

    Use a method (c.is_digit(), s.trim()), an operator (a + b on strings), or a qualified name (strings::length(s)).

conventions_prelude.tr
fn main(): int {    printline(abs(-5));    printline(math::max(1.5, 0.5));    printline(math::PI);    mut c: char = '7';    printline(c.is_digit());    printline(strings::length("turf"));    printline("turf".length());    return 0;}

$ turf run conventions_prelude.tr

5 1.5 3.14159 true 4 4

Four rules cover the rest:

  • The six dotted modules have no bare names. is_digit(c) is E041; write c.is_digit().
  • Constants stay qualified. math::PI, never a bare PI.
  • Your own names win. A bare name is the last thing looked up, so a function of your own with the same name hides the prelude’s. math::max(...) still reaches the original.
  • Everything else needs an import. See the table above.

The full lookup order and the one trade-off it carries are in Functions and modules: the prelude.

1. Naming

Every public function, method and field is snake_case (index_of, try_get, is_digit, to_upper). Types are PascalCase (List, StringBuilder, TcpConnection) and constants UPPER_CASE (PI, CLOCKS_PER_SEC). The library has one spelling for each name, with no aliases. A script checks this on every CI run (scripts/check/stdlib_surface_check.sh).

2. Failure: three kinds, three shapes

A function reports failure in the way that matches why it can fail.

Why it fails Shape Examples
The input or the environment: a file that is not there, text that is not a number, a refused connection. Not a bug in the caller. returns !T, with an error variant that carries what failed (the path, the offset, the name) and a message saying so (err.message) io::read_to_string, io::write_string, fs::mkdir, os::set_env, strings::parse_int, json::parse, math::divide, net::TcpConnection::connect, Map.try_get
A violated precondition: popping an empty stack, an index out of range, random_int(7, 3). A bug in the caller that no recovery makes correct. panics, and the message starts with the caller’s file:line:, so you learn which of your calls was wrong, not which line of the library noticed List.pop, Stack.pop, Queue.dequeue, Map.get, strings::substring, JsonValue.get, random::random_int
A question: is it there, is it a directory, does it start with this? Absence is a normal answer. returns bool, or a sentinel (-1 from index_of, "" from os::env for an unset variable) io::exists, fs::is_dir, Set.contains, strings::index_of

Some failures inside a function, such as an array index past the end or an integer overflow, are panics reported by the language; see Memory and safety: checks that panic.

Where a call has both a panicking and a recoverable form, they are a pair, and a predicate lets you ask first:

Panics on a miss Recoverable twin Ask first
Map.get(k) Map.try_get(k): !V Map.contains(k)
JsonValue.get(k) none JsonValue.has(k)
http::Request.get_header(n) none Request.has_header(n)
http::Server.get_route(p) none Server.has_route(p)

A !void result is handled like any other !T: fs::mkdir(p) recover run { ... }; runs the block only on failure (Error handling). Leaving the result unhandled discards the failure silently, so check anything whose failure matters.

conventions_failure_shapes.tr
import fs;fn main(): int {    mut dir: string = "/tmp/turf_stdlib_readme_dir";    fs::mkdir(dir) recover run {        printline("could not create the directory");        return 1;    };    fs::remove_dir(dir) recover run { printline("could not remove it"); };    printline(fs::is_dir(dir));       // false: a question, so a bool    return 0;}

$ turf run conventions_failure_shapes.tr

false

3. Resources and defer

Turf has no finalizers: the collector reclaims memory, not file descriptors. A value that owns an operating-system resource has an explicit close(), and the idiom is defer, which runs on every way out of the block, including each pass of a loop:

conventions_resources_and_defer.tr
import net;fn main(): int {    mut conn: net::TcpConnection = net::TcpConnection::connect(127, 0, 0, 1, 8080) recover run {        printline("could not connect");        return 1;    };    defer conn.close();    conn.write("ping");    return 0;}

TcpConnection.close() and TcpListener.close() are idempotent (a second call does nothing), so an early explicit close alongside the defer is safe. raw_fd() returns the descriptor (-1 once closed) for passing to FFI.

4. Visibility: what is public

A module’s declarations are private unless marked pub (Functions and modules). The library follows this rule:

  • A function that does a job is public, whether or not the library itself calls it. io::input, test::assert_neq and strings::last_index_of are public because a program may want them.
  • A struct’s internals are public only if a real program does something with them. Request.method is public (a handler reads it); List’s buffer, Map’s hash index and Server’s route table are not.
  • Raw C bindings are private. The library wraps fopen, getenv, socket and the rest in checked Turf functions; the wrappers are the interface. The exceptions are the bindings that are public as they stand (mem::malloc, time::usleep, sys::exit, the libm functions in math), which are part of the surface.

A program that needs a C function the library does not wrap declares its own extern fn for it (FFI and unsafe).

5. Stability: what 2.x guarantees

The public items of the standard library are the 2.x compatibility surface: a pub function, method, type, error, constant or field is not removed, renamed, or given a different signature or failure shape before 3.0. Additions are allowed in a minor release. This is checked, not just intended:

  • tests/46_stdlib_surface/ compiles and runs a use of every public item, so a removal or rename breaks a test.
  • scripts/check/stdlib_surface_check.sh fails if a public item exists that no file there mentions (a new name added without being locked) or that is not snake_case. It runs in CI.
  • Every module has its own behaviour tests.

Three things are not part of the surface: the behaviour of json::parse for \uXXXX escapes (a known limitation, see JSON), the iteration order of Map.keys() after a remove(), and mem::malloc_usable_size and mem::usable_size, which depend on the C library and may be unavailable on a platform.

Esc

Type to search.