Skip to content

Built-in types and functions ​

The types and functions below are part of the compiler, not of a library, so they have no page in the library reference. The libraries' own types, such as TextWriter, Timer, Environment, HashMap and the async sockets, are in lib/core and lib/std. Every example here is compiled and run by the guide's checks, so the names and signatures are the compiler's. With the MCP server, ceesafe_api looks any of these names up.

Console ​

FunctionType
Console.write(x), Console.writeLine(x)x an integer, float, bool, StringView or String
Console.writeBytes(bytes)bytes: Slice<u8>, raw bytes to standard output, UTF-8 or not
Console.writeError(text), Console.writeErrorLine(text)text: StringView, to standard error
Console.read(mut buffer)Result<usize, IoError>; 0 at the end of input

Console blocks its thread: in an async fn, use lib/std's AsyncConsole (N0295 otherwise).

Console.write takes text, which is always UTF-8. Bytes from a file or the network may not be: StringView.fromUtf8(bytes)! stops the program on them. To pass them through as they are, write them with Console.writeBytes(bytes).

ceesafe
fn main() {
    Console.write("answer: ");
    Console.writeLine(42);
    let bytes = "raw\n";
    Console.writeBytes(bytes.bytes());
    Console.writeLine(2.5);
    Console.writeLine(true);
}
text
answer: 42
raw
2.5
true

Process and program arguments ​

FunctionType
Process.argumentCount()usize, including the program name
Process.argument(i)StringView?: null out of range or for bytes that are not UTF-8
Process.exit(status)status: i32; never returns
Process.run(program, arguments)Result<i32, IoError>; arguments: borrow Vector<StringView>
Process.capture(program, arguments, input)Result<ProcessOutput, IoError> (lib/std)

Environment variables are lib/std's Environment.variable(name), a StringView?.

ceesafe
fn main() {
    Console.writeLine(Process.argumentCount() >= 1);
    match (Process.argument(1000)) {
        Some(a) => { Console.writeLine(a); }
        null => { Console.writeLine("no argument 1000"); }
    }
}
text
true
no argument 1000

File, TcpListener and TcpStream ​

FunctionType
File.open(path), File.create(path)Result<File, IoError> (reading; writing, created or truncated)
File.remove(path)Result<void, IoError>
f.read(mut buffer)Result<usize, IoError>, 0 at the end; buffer: SliceMut<u8>
f.write(bytes)Result<void, IoError>: all of them or an error
File.openReadWrite(path), File.openOrCreate(path), File.createNew(path)Result<File, IoError> for reading and writing, never truncated: an existing file; created if missing; a new one (AlreadyExists otherwise)
f.readAt(mut buffer, offset), f.writeAt(bytes, offset)positional: usize (0 past the end); every byte or an error
f.syncData(), f.syncAll()durable on disk (F_FULLFSYNC on Apple systems); after a failed sync the file's durable contents are unknown
f.length(), f.setLength(n)u64; truncate or zero-extend
File.rename(from, to), Directory.sync(path)atomic replace; make a directory's entries durable
Directory.space(path)the volume's total, free and available bytes (ADR 0077)
Console.flush()write standard output's buffer now, so it survives a kill
File.openAppend(path)write-only, created if missing, every write at the end
File.modified(path), File.setPermissions(path, 0o640)a UnixTime; chmod
File.openRegular(path)read-only, only a regular file: never waits on a FIFO; IsDirectory, or InvalidInput for a device (ADR 0078)
f.modified(), f.kind()the open file's time and PathKind, from the file, not its path
IoError.StorageFullthe volume is full (ENOSPC, EDQUOT)
TcpListener.bind(address, port)Result<TcpListener, IoError>; then port(), accept()
TcpStream.connect(address, port)Result<TcpStream, IoError>; read, write, shutdownWrite, shutdownRead
e.message()the IoError's text, a StringView

A File closes when it is destroyed. These block their thread: async code uses lib/std's AsyncFiles, AsyncTcpListener and AsyncTcpStream.

ceesafe
fn main() {
    match (File.open("/no/such/file")) {
        Ok(_) => { Console.writeLine("opened"); }
        Err(e) => { Console.writeLine(e.message()); }
    }
}
text
No such file or directory

StringView and String ​

MemberType
s.lengthusize, bytes
s[i], s[lo..hi], s.bytes()u8; a StringView (character boundaries checked); Slice<u8>
StringView.fromUtf8(bytes)StringView?: null for bytes that are not UTF-8
==, !=compare the bytes
String.create(), String.withCapacity(n)String; Result<String, AllocError>
t.append(view), t.appendNumber(n)Result<void, AllocError>
t.appendHex(n, minWidth, upper), t.appendOctal(n), t.appendFixed(f, digits), t.appendPadded(view, width, align, fill)Result<void, AllocError>: Rust's {:08x}, {:o}, {:.3}, {:>8} (also on a Vector<u8>, and as VectorBytes.appendHex(mut v, …))
t.view(), t.clear(), t.length, t.capacitya StringView borrowed from t; …
s.indexOf(t), s.lastIndexOf(t), s.indexOfByte(b)usize?
s.contains(t), s.startsWith(t), s.endsWith(t), s.equalsIgnoreAsciiCase(t)bool
s.trim(), s.trimStart(), s.trimEnd()the StringView without ASCII whitespace
s.splitOnce(sep), s.split(sep)TextHalves? (left, right); a TextSplit, whose next() gives each piece, then null
s.parseInt<T>(), s.parseIntRadix<T>(radix), s.parseFloat<T>()T?: null for text that is not a T

The searches and parsers work on a String too, and on bytes: a Slice<u8> (or a Vector<u8>) takes and gives Slice<u8> where a view takes and gives StringView. Every view they return stays valid UTF-8. Floats parse correctly rounded, and accept inf, infinity and nan in any case.

A StringView is a reference: a variable holding one is a let, and a struct or enum holding one needs a region parameter. For formatting into a fixed buffer without allocating, use lib/core's TextWriter (text, byte, bytes, decimal for a u64, signed for an i64, hex, then written).

ceesafe
fn main() {
    let s: StringView = "CeeSafe";
    Console.writeLine(s.length);
    Console.writeLine(s[0..3]);
    var t = String.create();
    t.append("x = ")!;
    t.appendNumber(-7)!;
    Console.writeLine(t.view());
    let bytes = [104u8, 105];
    match (StringView.fromUtf8(bytes[..])) {
        Some(v) => { Console.writeLine(v); }
        null => { Console.writeLine("not UTF-8"); }
    }
}
text
7
Cee
x = -7
hi

Vector, arrays and slices ​

MemberType
Vector<T>.create(), Vector<T>.withCapacity(n)Vector<T>; Result<Vector<T>, AllocError>
v.push(x)Result<void, AllocError>
v.pop()T?
v.length, v.capacity, v[i]usize; an element place
v.clear(), v.truncate(n), v.swap(i, j)
v.insert(at, x), v.remove(at), v.swapRemove(at)Result<void, AllocError>; T; T (the last element fills the gap); the index is checked at the call
[]the empty Slice<T> or SliceMut<T>, where one is expected
a.writeLE(0, x) on an arrayan array's methods are its slice's: a[..].writeLE(0, x)
v.view(), v.viewMut()Slice<T>, SliceMut<T>
[x; N], [a, b, c], a[lo..hi], borrow mut a[lo..hi]an array; a Slice<T>; a SliceMut<T>
d.copyFrom(s), d.fill(v)bulk writes into a SliceMut<T>
v.extend(s), v.resize(n, fill), v.reserve(extra)Result<void, AllocError>; one growth at most; extend and resize need Copy elements
b.readBE<T>(at), b.readLE<T>(at)T, an integer from bytes, big- or little-endian
b.writeBE(at, x), b.writeLE(at, x)into a SliceMut<u8> (or a mutable Vector<u8>)
v.pushBE(x), v.pushLE(x)Result<void, AllocError>: x's bytes appended to a Vector<u8>

Byte order works on fixed-width integers (u8 to u64, i8 to i64); the value's type gives the width, so a literal needs a suffix (v.pushBE(5u32)). A read or write past the end panics at the call, and writes nothing.

ceesafe
fn main() {
    var message = Vector<u8>.create();
    message.pushBE(0x51u8)!; // 'Q'
    message.pushBE(0u32)!; // the length, patched below
    message.extend("SELECT 1".bytes())!;
    message.pushBE(0u8)!;
    let length = truncate<u32>(message.length - 1);
    message.writeBE(1, length);
    Console.writeLine(message.readBE<u32>(1));
    var fields = "r=abc,s=xyz,i=4096".split(",");
    while (true) {
        let field = match (fields.next()) {
            Some(f) => f,
            null => { break; }
        };
        let pair = field.splitOnce("=")!;
        if (pair.left == "i") {
            Console.writeLine(pair.right.parseInt<u32>()! * 2);
        }
    }
    Console.writeLine(" 2.5e-3 ".trim().parseFloat<f64>()!);
}
text
13
8192
0.0025

Indexes are usize. A range of two literals that the body indexes with is a usize range. Arrays and slices may hold views ([StringView; 3], Slice<StringView>), but not borrow T references. Slice a variable, not a call's result: bind it to a local first.

ceesafe
fn total(xs: Slice<u32>) -> u32 {
    var n: u32 = 0;
    for (x in xs) {
        n += x;
    }
    return n;
}

fn main() {
    var v = Vector<u32>.create();
    for (i in 0u32..4) {
        v.push(i * 10)!;
    }
    Console.writeLine(total(v.view()));
    var buffer = [0u32; 4];
    buffer[1..3].fill(5);
    Console.writeLine(total(buffer[..]));
    let names = ["Low", "Medium", "High"];
    Console.writeLine(names[1]);
    match (v.pop()) {
        Some(last) => { Console.writeLine(last); }
        null => {}
    }
}
text
60
10
Medium
30

Integers, floats and conversions ​

OperationNotes
+ - * / %, unary -panic on overflow, and on dividing by zero
& | ^ << >>unsigned types only; no ~ (use x ^ 0xFFFFFFFF)
x.wrappingAdd(y), x.checkedAdd(y), x.saturatingAdd(y)also Sub, Mul; checked… is a T?
x.min(y), x.max(y)every integer type
x.abs(), x.unsignedAbs()signed types: abs panics at the minimum, unsignedAbs is the unsigned type of the width
x.rotateLeft(n), x.rotateRight(n), x.wrappingShl(n), x.wrappingShr(n)unsigned types; n: u32, taken modulo the width
x.leadingZeros(), x.trailingZeros(), x.countOnes()unsigned types; a u32 (the width for 0)
x.byteSwap()every integer type: the bytes in the other order
x as Tonly conversions that never lose information; a as u64 < b compares
*rthe Copy value r refers to; *r = v writes through a borrow mut
replace(mut place, v), swap(mut a, mut b)the old value, which nothing is left without; exchange two places
assert(cond), assert(cond, message), panic(message)panic when false; always (never)
debugAssert(cond), debugAssert(cond, message)as assert, but only in debug builds, tests and the interpreter; may change nothing that exists before it (Debugging)
embed("path"), embedText("path")a file's bytes as a Slice<u8>, or as UTF-8 text, read when the package is checked; the path is relative to the source file and stays inside the package (Constants)
r.isOk(), r.isErr(), r.ok(), r.err(), o.okOr(e)a Result's tests and conversions; an optional as a Result
enum E { @from Io(IoError), … }? turns an IoError into E.Io(e) in a function returning Result<_, E>
truncate<T>(x)wraps an integer; a float to an integer rounds toward zero and panics out of range or on NaN
checkedTruncate<T>(f), saturatingTruncate<T>(f)a float to an integer: T? (null where truncate would panic); the nearest bound (NaN gives 0)
round<T>(x)to a float type, rounding to nearest
f.sqrt(), f.abs(), f.floor(), f.ceil(), f.min(g), f.max(g), f.bits()floats
ceesafe
fn main() {
    let big: u8 = 250;
    Console.writeLine(big.wrappingAdd(10));
    Console.writeLine(big.saturatingAdd(10));
    let n: i32 = -7;
    Console.writeLine(n.abs());
    Console.writeLine(n.max(3));
    let wide: i64 = 300;
    Console.writeLine(truncate<u8>(wide));
    let f: f64 = 2.75;
    Console.writeLine(truncate<i32>(f));
    Console.writeLine(round<f32>(wide));
    Console.writeLine(f.floor());
}
text
4
255
7
3
44
2
300
2

Box and memory ​

Type or functionNotes
Box.create(value)Result<Box<T>, AllocError>, heap-owned; Box<dyn I> for dynamic dispatch
FixedBufferAllocator.over(mut bytes)an allocator over a caller's buffer, for freestanding code
Arena, Scratch, Pool<T>, SlotMap<T>, StableVector<T>see memory and collections
Thread.spawn, Thread.both, Async.run, Async.scopesee threads and async
ceesafe
fn main() {
    let b = Box.create(41)!;
    Console.writeLine(b + 1);
}
text
42

Every signature above ​

This block is type-checked, not run: each call is annotated with the type the tables give, so a table that drifts from the compiler fails the guide's checks.

ceesafe
fn everything() -> Result<void, IoError> {
    let created: Result<File, IoError> = File.create("/tmp/x");
    var f = created?;
    let wrote: Result<void, IoError> = f.write("hi".bytes());
    var buffer = [0u8; 8];
    let got: Result<usize, IoError> = f.read(mut buffer[..]);
    let removed: Result<void, IoError> = File.remove("/tmp/x");
    let input: Result<usize, IoError> = Console.read(mut buffer[..]);
    Console.writeError("e");
    Console.writeErrorLine("e");
    var listener = TcpListener.bind("127.0.0.1", 0)?;
    let port: Result<u16, IoError> = listener.port();
    let accepted: Result<TcpStream, IoError> = listener.accept();
    var stream = TcpStream.connect("127.0.0.1", 1)?;
    let n: Result<usize, IoError> = stream.read(mut buffer[..]);
    let w: Result<void, IoError> = stream.write(buffer[..]);
    let sw: Result<void, IoError> = stream.shutdownWrite();
    let sr: Result<void, IoError> = stream.shutdownRead();
    var arguments = Vector<StringView>.create();
    let status: Result<i32, IoError> = Process.run("true", borrow arguments);
    let captured: Result<ProcessOutput, IoError> = Process.capture("true", borrow arguments, "".bytes());
    let home: StringView? = Environment.variable("HOME");
    return Ok();
}
fn collections() -> Result<void, AllocError> {
    var v: Vector<u32> = Vector<u32>.withCapacity(4)?;
    v.push(1)?;
    v.push(2)?;
    let c: usize = v.capacity;
    v.swap(0, 1);
    v.truncate(1);
    let m: SliceMut<u32> = v.viewMut();
    v.clear();
    var t: String = String.withCapacity(8)?;
    t.clear();
    let tc: usize = t.capacity;
    var dst = [0u8; 2];
    let src = [1u8, 2];
    dst[..].copyFrom(src[..]);
    var bytes = Vector<u8>.create();
    bytes.extend(src[..])?;
    bytes.resize(8, 0)?;
    bytes.reserve(8)?;
    bytes.pushBE(1u16)?;
    bytes.pushLE(-1i32)?;
    let word: u32 = bytes.readBE<u32>(0);
    let back: i16 = bytes.view().readLE<i16>(2);
    bytes.writeLE(0, 7u64);
    dst[..].writeBE(0, 3u16);
    return Ok();
}
fn text() {
    let s: StringView = " a,b ";
    let at: usize? = s.indexOf(",");
    let last: usize? = s.lastIndexOf("b");
    let byte: usize? = s.indexOfByte(',');
    let has: bool = s.contains("a") && s.startsWith(" ") && s.endsWith(" ") && s.equalsIgnoreAsciiCase(" A,B ");
    let t: StringView = s.trim().trimStart().trimEnd();
    let halves: TextHalves? = t.splitOnce(",");
    var pieces: TextSplit = t.split(",");
    let piece: StringView? = pieces.next();
    let n: i64? = "-12".parseInt<i64>();
    let h: u8? = "ff".parseIntRadix<u8>(16);
    let f: f32? = "1.5".parseFloat<f32>();
    let raw: Slice<u8> = s.bytes();
    let rawAt: usize? = raw.indexOf(",".bytes());
    let rawTrim: Slice<u8> = raw.trim();
    let rawHalves: ByteHalves? = raw.splitOnce(",".bytes());
    var rawPieces: ByteSplit = raw.split(",".bytes());
    let rawN: u32? = raw.parseInt<u32>();
}
fn numbers() {
    let a: u8 = 250;
    let s: u8 = a.wrappingSub(1);
    let m: u8? = a.checkedMul(2);
    let x: i64 = -5;
    let u: u64 = x.unsignedAbs();
    let f: f64 = 2.0;
    let ct: u8? = checkedTruncate<u8>(f);
    let st: u8 = saturatingTruncate<u8>(f);
    let r: f64 = f.sqrt().abs().ceil().min(1.0).max(0.5);
    let bits: u64 = f.bits();
    let w: u32 = 0x80000001;
    let rotated: u32 = w.rotateLeft(1).rotateRight(2).wrappingShl(33).wrappingShr(1);
    let counts: u32 = w.leadingZeros() + w.trailingZeros() + w.countOnes();
    let swapped: i16 = (-2i16).byteSwap();
}
fn memory() {
    var storage = [0u8; 64];
    let arena = FixedBufferAllocator.over(mut storage);
}
fn main() {}

Pre-1.0: the language and its tools are still changing.