std/io
Synchronous blocking I/O.
MVP scope: stdin/stdout/stderr, file read/write, existence check. No async, no networking, no streaming primitives.
Types
IOErrorKind :: enum(u8) What went wrong, classified so a caller can branch on it without transcribing platform numbers.
A raw errno is not portable (EAGAIN is 35 on Darwin and 11 on Linux)
and Windows has no errno at all, only GetLastError with its own
numbering. This is the shared vocabulary; IOError.code keeps the raw value
for the cases it does not cover.
Variants
Other The failure carried no code, or one this does not classify. Read code.
NotFound PermissionDenied Interrupted Interrupted by a signal: the caller may retry.
AlreadyExists NotADirectory IsADirectory WouldBlock The operation would block on a non-blocking handle.
BrokenPipe NoSpace InvalidInput DirectoryNotEmpty BadHandle IOError :: struct Raised by every fallible operation in this module: the concrete error member
of each T | IOError return. Narrow with is IOError ; widen to Error for
a trait-typed slot.
Fields
msg: string Short human-readable diagnostic ("file not found", "EOF", ...).
kind: IOErrorKind = .Other Portable classification. .Other when the failure had no platform code
(an end-of-input, a ceiling this library imposes) or when the code is
one IOErrorKind does not name.
code: i32 = 0 The raw platform value: errno on POSIX, GetLastError on Windows.
Zero when the failure did not come from a system call. NOT portable:
branch on kind, and read this only to report or to test something
kind does not cover.
Stream :: enum A standard output stream. Used by the print / println / eprint /
eprintln wrappers below to pick a target, and exported so terminal-aware
code (e.g. std/tty::is_tty) can name a stream. The two variants carry tags
0 (Stdout) and 1 (Stderr), matching the runtime write primitive.
Variants
Stdout Stderr Functions
print :: fn<T: Display>(msg: T) -> void Write msg to stdout without a trailing newline. Accepts any type
implementing Display: the wrapper renders it via to_string
before handing the string to the host.
Parameters
| msg | T | No description. |
println :: fn<T: Display>(msg: T) -> void Write msg to stdout followed by a \n. See print for the
Display routing rule.
Parameters
| msg | T | No description. |
eprint :: fn<T: Display>(msg: T) -> void Write msg to stderr without a trailing newline.
Parameters
| msg | T | No description. |
eprintln :: fn<T: Display>(msg: T) -> void Write msg to stderr followed by a \n.
Parameters
| msg | T | No description. |
read_stdin :: fn(n: i64) -> string | IOError Read EXACTLY n bytes from stdin into a fresh UTF-8 string. Loops until n
bytes are accumulated; end of input before n is an error. The bytes come as
they are, \r\n included: required by length-prefixed transports (LSP, MCP,
custom RPC).
Parameters
| n | i64 | number of bytes to consume. |
Returns
string | IOError a string of exactly n bytes (interpreted as UTF-8).Errors
IOErroron end of input, a failed read, or a target with no stdin.
read_available :: fn(max: i64) -> string | IOError Read what stdin has ready: block until at least one byte has arrived, then
return what has, up to max bytes. Unlike read_stdin, it waits for no
count, which is what reading keys as they are pressed needs.
Parameters
| max | i64 | ceiling on the bytes returned. |
Returns
string | IOError at least one byte.Errors
IOErrorat end of input, on a failed read, on a target with no stdin, and.InvalidInputwhenmaxis not positive.
read_available_or_null :: fn(max: i64) -> string | null read_available with null in place of its error, for a caller that only
tells a read from its absence.
Parameters
| max | i64 | ceiling on the bytes returned. |
Returns
string | null poll_stdin :: fn(timeout_ms: i32) -> bool True iff stdin has data ready within timeout_ms (0 = non-blocking
probe). Lets a length-prefixed server detect a quiescent input window,
e.g. drain a burst of notifications, then do expensive work once. A pipe is
read straight from its descriptor, so no buffer lags behind what this
reports; a console reports ready. A closed peer (pipe hangup)
reports ready so the next read observes EOF rather than spinning.
Parameters
| timeout_ms | i32 | max wait in milliseconds ; 0 polls without blocking. |
Returns
bool true if a subsequent read_stdin won't block on the first byte (data ready, or EOF/hangup pending).read_file_bytes :: fn(path: string, max_bytes: i64 = READ_LIMIT_DEFAULT) -> u8[] | IOError Read a file fully into a fresh owned byte array. Lossless for arbitrary
binary: the bytes are taken verbatim, no UTF-8 reinterpretation. This is
the canonical file-read primitive ; read_file_string decodes on top.
A first chunk is the whole of most files. Past it, a regular file is read in
one go into an array of its size. Anything else is read by chunks to its end
or to max_bytes: a pipe or a device, a file that grew, and one whose size
passes max_bytes, a size being a claim the content may not bear out. On
Windows, a read short of what it asked for ends the file, a pipe's included.
Parameters
| path | string | filesystem path (absolute or cwd-relative). |
| max_bytes | i64 = READ_LIMIT_DEFAULT | the most bytes accepted, READ_LIMIT_DEFAULT by default: the read is refused once it passes them, which also stops a stream that never ends. Name your own when you know what you are reading, a larger file included. |
Returns
u8[] | IOError the file's raw bytes.Errors
IOErrorif the file can't be opened or read, or if it passesmax_bytes:"file too large".
write_file_bytes :: fn(path: string, content: u8[]) -> null | IOError Write raw bytes to path, replacing any existing file. Lossless for
arbitrary binary. The canonical file-write primitive.
Parameters
| path | string | filesystem path. |
| content | u8[] | the raw bytes to write. |
Returns
null | IOError Errors
IOErrorif the file can't be opened or written.
create_dir :: fn(path: string) -> null | IOError Create path and every missing parent, the way mkdir -p does.
An existing directory is success, not an error: the caller asked for it to exist, and it does. Permissions follow the process umask.
Parameters
| path | string | directory to create, /-separated. |
Returns
null | IOError null on success.Errors
IOErrorwhen a component cannot be created: a parent that is a file, a read-only mount, a path over 4096 bytes.
remove_file :: fn(path: string) -> null | IOError Delete the file at path.
Files only. A directory is refused by the platform rather than silently
recursed into: the conservative default for something irreversible. A
missing path is an error too: a caller that does not care checks exists
first, and one that does would rather hear about it.
Parameters
| path | string | file to delete. |
Returns
null | IOError null on success.Errors
IOErrorwhen the path is missing, is a directory, or is not removable.
read_file_string :: fn(path: string, max_bytes: i64 = READ_LIMIT_DEFAULT) -> string | IOError Read a file fully into a UTF-8 string: a thin text decode over
read_file_bytes. Use read_file_bytes directly for binary data.
Parameters
| path | string | filesystem path (absolute or cwd-relative). |
| max_bytes | i64 = READ_LIMIT_DEFAULT | the most bytes accepted, READ_LIMIT_DEFAULT by default; see read_file_bytes. |
Returns
string | IOError file contents as a UTF-8 string.Errors
IOErrorif the file can't be opened or read, or if it passesmax_bytes.
write_file_string :: fn(path: string, content: string) -> null | IOError Write a UTF-8 string to path, replacing any existing file: a thin
encode over write_file_bytes. Use write_file_bytes directly for binary.
Parameters
| path | string | filesystem path. |
| content | string | the UTF-8 string to write. |
Returns
null | IOError Errors
IOErrorif the file can't be opened or written.
exists :: fn(path: string) -> bool True iff path resolves to an existing filesystem entry (file or
directory). Errors (e.g. permission denied) collapse to false.
Parameters
| path | string | filesystem path to probe. |
Returns
bool true if the entry exists, false otherwise (including on access errors).is_dir :: fn(path: string) -> bool True iff path exists AND is a directory. Errors collapse to false.
Read permission on the directory is NOT required: traverse rights on its
parent are enough, which is what stat asks for.
Parameters
| path | string | filesystem path to probe. |
Returns
bool true iff path names a directory.modification_time :: fn(path: string) -> i64 | IOError When path was last modified, in nanoseconds since the Unix epoch : what a
cache compares to learn that a file changed without reading it.
Parameters
| path | string | filesystem path to probe. |
Returns
i64 | IOError Errors
IOErrorwhenpathcannot be probed :NotFoundwhen it does not exist.
real_path :: fn(path: string) -> string | IOError path as the filesystem resolves it: absolute, /-separated, every symbolic
link followed and every . and .. gone. Two spellings of one file give one
answer (/tmp and /private/tmp under macOS), which a lexical normalization
(std/path) cannot promise.
Parameters
| path | string | an existing file or directory. |
Returns
string | IOError Errors
IOErrorwhenpathcannot be resolved:NotFoundwhen it does not exist.
read_dir :: fn(path: string) -> string[] | IOError List the immediate entries of path in OS-provided order. Excludes
. and ... Names are relative: join against path (e.g. via
std/path) for full paths.
Parameters
| path | string | the directory to list. |
Returns
string[] | IOError entry names (no path prefix).Errors
IOErrorifpathisn't a directory or can't be opened.
current_executable_location :: fn() -> string Full path of the running executable, /-separated. Use std/path's
parent to get its directory, e.g. to resolve sidecar resources (stdlib,
C runtime) next to the binary. Falls back to "." if the OS query fails.
Returns
string the executable's own path.current_working_directory :: fn() -> string The directory the process resolves relative paths against, /-separated and
WITHOUT a trailing separator: where it was started, unless
set_current_working_directory moved it. Join with "/name" to build a path. Distinct from
current_executable_location, which answers where the binary lives: use
this one to resolve a path the user typed, that one for resources shipped
beside the executable. Falls back to "." if the OS query fails.
Returns
string the working directory.set_current_working_directory :: fn(path: string) -> null | IOError Make path the directory the process resolves relative paths against.
Process-wide: every thread, and every child spawned afterwards, sees it.
Parameters
| path | string | the new working directory, absolute or relative to the current one. |
Returns
null | IOError Errors
IOErrorwhenpathis not a directory the process may enter.
temp_dir :: fn() -> string OS scratch directory for temporary files, /-separated and WITHOUT a
trailing separator: join with "/name" to build a path. Honours the
platform temp-dir environment ($TMPDIR on POSIX, %TMP% / %TEMP% via
GetTempPath on Windows), falling back to "/tmp" when unset. The
directory is assumed to exist and be writable ; this does not create it.
Returns
string the temp directory path, e.g. "/tmp" or "/var/folders/.../T".Constants
READ_LIMIT_DEFAULT: i64: 16 * 1024 * 1024 A ceiling for a read whose caller has no figure of its own.
It is a policy number, not a fact about files, and it is bounded from BOTH
sides. It has to clear anything the toolchain legitimately reads: the
largest .vader source here is 161 KiB and the largest emitted seed unit
2.3 MiB. And it has to stay under what the smallest backend can hold, or it
never fires: the interpreter carries a u8 per slot, so 256 MiB of file
costs it about 2 GB and the GC traps before the refusal can be returned.
16 MiB clears the first by ~100x and refuses cleanly on both backends.
Name your own when you know what you are reading.