sys
sys is the one layer of the standard library that talks to the operating system directly. Everything above it — String, the collections, the I/O buffering, the upper net layers — is pure Cryo. Below it sits each platform's native system interface, selected per target.
public module sys::syscall;
| File | What it holds | Import |
|---|---|---|
syscall | Syscall numbers, sys_callN, the typed sys_* wrappers, and the Win32 / NT surfaces | import std::sys::syscall; |
The seam functions below live at the root of the module: import std::sys;.
This is a low-level seam, not the everyday I/O surface. Almost everything you want is one layer up in
io,fs, orffi::libc.
Backends
Each seam function is declared twice, gated with ![target(...)]:
- Linux — the raw kernel syscall, issued through inline assembly with no libc involvement. On Linux the kernel syscall interface is the stable ABI; glibc is a wrapper over it.
- Everything else — a thin pass-through to
ffi::libc. On Windows the eventual native surface is kernel32 and ntdll, since there the DLLs are the stable ABI.
The error model
Every fallible sys function returns a signed integer.
- A negative result is
-errno— the negated POSIX error number, so-EINTRis-4and-EBADFis-9. - A non-negative result is the success value: a byte count, a file descriptor, or
0.
This is normalized at the seam, independent of the backend. A raw Linux syscall already returns -errno in the result register and is passed through as-is; the libc arms read errno after a -1 return and negate it.
Nothing here retries or builds a rich error. A caller that wants Result and IoError and EINTR retry builds that on top — that policy lives above sys, in io::fd.
The seam surface
| Function | Returns |
|---|---|
read(fd, buf, count) | Bytes read (0 is EOF), or -errno. |
write(fd, buf, count) | Bytes written — short writes are normal. |
open(path, flags, mode) | The new fd, or -errno. |
close(fd) | 0, or -errno. |
mmap(addr, length, prot, flags, fd, offset) | The mapped address as a non-negative i64. |
munmap(addr, length) | 0, or -errno. |
exit(code) | Does not return. |
A few details that bite if you don't know them:
read and write are single raw calls with no EINTR retry — loop on -EINTR yourself if you need it.
open forces O_BINARY on the libc arms. The seam presents POSIX byte semantics, and MSVCRT would otherwise default the descriptor to text mode, where CRLF translation makes byte counts and contents stop matching what was passed. On POSIX targets O_BINARY is 0, so it costs nothing.
mmap is a Unix-only seam surface; Windows callers use VirtualAlloc through sys::syscall directly, which is per-DLL rather than per-libc so there is nothing to wean off. x86-64 user addresses fit comfortably in the positive i64 range, so the sign carries the success/failure distinction losslessly.
exit is exit_group(2) natively, ending every thread in the process — Syscall::exit alone would end only the calling thread. It runs no libc atexit handlers, and unflushed buffered writers lose their contents, so flush first.
function read(fd: i32, buf: u8*, count: u64) -> i64;
function read(fd: i32, buf: u8*, count: u64) -> i64;read(2): read up to count bytes from fd into buf. Returns the byte count read (0 == EOF), or -errno on failure. A single raw call — no EINTR retry; a caller that wants retry loops on -EINTR itself.
function write(fd: i32, buf: u8*, count: u64) -> i64;
function write(fd: i32, buf: u8*, count: u64) -> i64;write(2): write up to count bytes from buf to fd. Returns the byte count written (short writes are normal), or -errno on failure.
function open(path: u8*, flags: i32, mode: u32) -> i64;
function open(path: u8*, flags: i32, mode: u32) -> i64;open(2): open path (a NUL-terminated C string) with flags and, when creating, mode. Returns the new file descriptor (>= 0), or -errno. The O_* flag values pass through unchanged: on Linux, glibc's O_* constants are the kernel's own bit values.
function close(fd: i32) -> i64;
function close(fd: i32) -> i64;close(2): release fd. Returns 0 on success, or -errno.
function mmap(addr: u8*, length: u64, prot: i32, flags: i32, fd: i32, offset: i64) -> i64;
function mmap(addr: u8*, length: u64, prot: i32, flags: i32, fd: i32, offset: i64) -> i64;mmap(2): map length bytes of memory. Returns the mapped address as a non-negative i64, or -errno on failure. Unlike the fd calls, mapping is a unix-only seam surface (Windows callers use VirtualAlloc through sys::syscall directly — per-DLL, not per-libc, so there is nothing to wean). x86-64 user addresses fit comfortably in the positive i64 range, so the sign carries the success/failure distinction losslessly.
function munmap(addr: u8*, length: u64) -> i64;
function munmap(addr: u8*, length: u64) -> i64;munmap(2): unmap [addr, addr+length). Returns 0 on success, or -errno.
function exit(code: i32) -> void;
function exit(code: i32) -> void;_exit(2)-style process termination with status code. Does not return. Unflushed buffered writers lose their contents; flush before calling.
public function name() { ... }
public:Visible to any module that imports this one - the default for top-level items and for struct fields, so it is mostly written to be explicit. public: opens a visibility block that groups the members after it.
public module collections::array;In a directory's _module.cryo aggregator, lists a submodule and whether it is public. Importing the directory's namespace sees only the modules declared public here.
type struct String<A = GlobalAlloc> {
buffer: RawBuffer<u8, A>;
length: u64;
}String: owned, heap-allocated, length-typed UTF-8.
std::collections::stringimport a::b;
import a::b::{ X, Y };
import a::b as c;
import a::b::*;Brings a module, or selected items from it, into scope. One path per declaration: use the brace form for several items, as to alias, and * for everything public (prefer the brace form; wildcards invite name collisions).
type struct IoError {
kind: IoErrorKind;
os_code: i32;
}function read(fd: i32, buf: u8*, count: u64) -> i64read(2): read up to count bytes from fd into buf. Returns the byte count read (0 == EOF), or -errno on failure. A single raw call — no EINTR retry; a caller that wants retry loops on -EINTR itself.
function write(fd: i32, buf: u8*, count: u64) -> i64write(2): write up to count bytes from buf to fd. Returns the byte count written (short writes are normal), or -errno on failure.
function open(path: u8*, flags: i32, mode: u32) -> i64open(2): open path (a NUL-terminated C string) with flags and, when creating, mode. Returns the new file descriptor (>= 0), or -errno. The O_* flag values pass through unchanged: on Linux, glibc's O_* constants are the kernel's own bit values.
function mmap(addr: u8*, length: u64, prot: i32, flags: i32, fd: i32, offset: i64) -> i64mmap(2): map length bytes of memory. Returns the mapped address as a non-negative i64, or -errno on failure. Unlike the fd calls, mapping is a unix-only seam surface (Windows callers use VirtualAlloc through sys::syscall directly — per-DLL, not per-libc, so there is nothing to wean).
function offset<T>(ptr: T*, count: i64) -> T*Offset a typed pointer by count elements (not bytes). Negative values walk backwards.
function munmap(addr: u8*, length: u64) -> i64munmap(2): unmap [addr, addr+length). Returns 0 on success, or -errno.
function exit(code: i32) -> void_exit(2)-style process termination with status code. Does not return. Unflushed buffered writers lose their contents; flush before calling.
extern "C" function syscall(number: Syscall, ...) -> i64type enum Syscall : i64 {
read = 0;
write = 1;
open = 2;
close = 3;
stat = 4;
fstat = 5;
lstat = 6;
poll = 7;
lseek = 8;
mmap = 9;
// … 334 more
}Linux x86_64 syscall identifier. Mutually exclusive: the kernel dispatches on the integer value in rax. Pass to syscall() or sys_callN(...); the i64 backing matches glibc's long syscall(long, ...) ABI exactly.
Syscall::exit = 60function name<T>(param: Type) -> Ret { ... }Declares a function. Parameters are written name: Type and are never inferred; the return type follows -> and defaults to void. Declaration order does not matter: every signature is collected before any body is checked, so functions may call each other freely and recurse.
function read(fd: i32, buf: u8*, count: u64) -> i64read(2): read up to count bytes from fd into buf. Returns the byte count read (0 == EOF), or -errno on failure. A single raw call — no EINTR retry; a caller that wants retry loops on -EINTR itself.
(parameter) fd: i32(parameter) buf: u8*(parameter) count: u64function write(fd: i32, buf: u8*, count: u64) -> i64write(2): write up to count bytes from buf to fd. Returns the byte count written (short writes are normal), or -errno on failure.
function open(path: u8*, flags: i32, mode: u32) -> i64open(2): open path (a NUL-terminated C string) with flags and, when creating, mode. Returns the new file descriptor (>= 0), or -errno. The O_* flag values pass through unchanged: on Linux, glibc's O_* constants are the kernel's own bit values.
(parameter) path: u8*(parameter) flags: i32(parameter) mode: u32function close(fd: i32) -> i64close(2): release fd. Returns 0 on success, or -errno.
function mmap(addr: u8*, length: u64, prot: i32, flags: i32, fd: i32, offset: i64) -> i64mmap(2): map length bytes of memory. Returns the mapped address as a non-negative i64, or -errno on failure. Unlike the fd calls, mapping is a unix-only seam surface (Windows callers use VirtualAlloc through sys::syscall directly — per-DLL, not per-libc, so there is nothing to wean).
(parameter) addr: u8*(parameter) length: u64(parameter) prot: i32(parameter) offset: i64function munmap(addr: u8*, length: u64) -> i64munmap(2): unmap [addr, addr+length). Returns 0 on success, or -errno.
function exit(code: i32) -> void_exit(2)-style process termination with status code. Does not return. Unflushed buffered writers lose their contents; flush before calling.
(parameter) code: i32extern "C" function _exit(code: i32) -> void