The Standard Library
Modules under std/ ship with the compiler and are always on the include
path. Load them with use.
I/O
std/io.cnd has print helpers and readers.
use "std/io.cnd";
fn main() -> i32 {
println("hello");
let n = 42;
print(&n, .I32);
let sp: []u8 = " ";
print(&sp, .S);
let big: u64 = 7;
print(&big, .U64);
putchar(10);
return 0;
}
$ ./io
hello
42 7
| Function | Writes |
|---|---|
print(&x, .Tag) | one value, selected by tag |
println(s) | string plus newline |
putchar(c) | one byte |
printerr(s) / printlnerr(s) | stderr |
read_byte() -> ?u8 | one byte from stdin, none on EOF |
The print tags are I32, I64, I128, U8, U64, U128, F64,
Bool, S (a []u8 string), and Ptr.
Vec
std/vec.cnd is a growable byte buffer. Functions take a pointer to the
struct; methods would be pointless without mutation.
use "std/io.cnd";
use "std/vec.cnd";
fn main() -> i32 {
let mut v = Vec { data: null, len: 0, cap: 0 };
vec_init(&v);
let ok = vec_push(&v, 10) && vec_push(&v, 20) && vec_push(&v, 30);
if !ok { return 1; }
let mut total: u32 = 0;
for i in 0..vec_len(&v) {
let b = vec_get(&v, i) else { return 2; };
total += b;
}
let out: i32 = total as i32;
print(&out, .I32);
putchar(10);
vec_deinit(&v);
return 0;
}
$ ./vec
60
vecfromslice(s) builds a vector from a string.
String
std/string.cnd is an owning, growable string built on Vec.
use "std/io.cnd";
use "std/string.cnd";
fn main() -> i32 {
let mut s = string_from("cin");
if !string_push_byte(&s, 'd' as u8) { return 1; }
if !string_append(&s, "er") { return 2; }
println(string_as_slice(&s));
string_deinit(&s);
return 0;
}
$ ./string
cinder
Str helpers
std/core/str.cnd is pure and allocation-free.
use "std/io.cnd";
use "std/core/str.cnd";
fn main() -> i32 {
let s = " cinder ";
let t = str_trim(s);
println(t);
if str_starts_with(t, "cin") {
println("starts with cin");
}
return 0;
}
$ ./str
cinder
starts with cin
Math, memory, ascii
std/core/math.cnd (mathmax, mathgcd, mathispow2, ...),
std/core/mem.cnd (memcopy, memzero, memalignup, ...), and
std/core/ascii.cnd (asciiisdigit, asciitoupper, ...) are tiny,
portable helpers.
Allocator
std/alloc.cnd exposes alloc(n), alloc_realloc(p, n), and dealloc(p)
backed by libc on hosted targets.
use "std/io.cnd";
use "std/alloc.cnd";
fn main() -> i32 {
let p = alloc(16);
if p == null { return 1; }
unsafe {
p[0] = 65;
p[1] = 66;
p[2] = 67;
p[3] = 0;
let slice = p[..3];
print(&slice, .S);
}
putchar(10);
dealloc(p);
return 0;
}
$ ./alloc
ABC
Command line arguments
main may declare the C-style signature main(argc: i32, argv: **u8)
to receive the argument count and a pointer to the argument strings.
argv[0] is the program path; real arguments start at argv[1].
use "std/io.cnd";
use "std/core/str.cnd";
fn cstr_slice(p: *u8) -> []u8 {
unsafe {
let len = strlen(p);
return p[..len];
}
}
fn main(argc: i32, argv: **u8) -> i32 {
let n: i32 = argc;
print(&n, .I32);
putchar(10);
for i in 1 .. argc {
unsafe {
println(cstr_slice(argv[i as usize]));
}
}
return 0;
}
$ ./args one two
3
one
two
File I/O
std/file.cnd reads and writes whole files through the C library. Paths are
[]u8 slices. filereadall returns a heap buffer (NUL-terminated) that you
must release with dealloc.
use "std/file.cnd";
use "std/io.cnd";
fn main() -> i32 {
let path = "/tmp/cinder_doc_file.txt";
if !file_write_all(path, "hello file") { return 1; }
let data = file_read_all(path) else { return 2; };
println(data);
unsafe { dealloc(data.ptr); }
return 0;
}
$ ./file
hello file
| Function | Behavior |
|---|---|
filereadall(path) -> ?[]u8 | whole file into a heap buffer |
filewriteall(path, data) -> bool | write, truncating existing file |
file_exists(path) -> bool | can the file be opened |
file_size(path) -> ?usize | size in bytes |
file_remove(path) -> bool | delete the file |
Port I/O and panic
std/x86.cnd declares outb/inb/etc. for bare-metal; std/panic.cnd
defines panic(msg) which aborts the program.