std::io
Basic stdin reads, stdout/stderr writes, an
fd-backed BufferedWriter, and a minimal std::io::async subset are
implemented in std/io.slk via std::runtime::io.
std::io provides console and basic stream I/O.
Hosted baseline: POSIX file descriptors and blocking I/O, with a minimal async
subset (std::io::async) backed by the hosted runtime on supported POSIX
hosts (linux/* and Apple Silicon macos/aarch64 today). Linux may use
io_uring acceleration where available; other supported POSIX hosts use the
runtime poll fallback.
See also:
- strings (formatting targets and string building)
- fmt (format string syntax)
- conventions (error conventions)
Exported API#
The current stdlib provides basic unbuffered stdio primitives
(stdin reads and stdout/stderr writes), a small formatting surface
(implemented without libc varargs; formatted bytes are written via
std::runtime::io::write), and a minimal async wrapper layer in
std::io::async:
module std::io;
enum IOErrorKind {
OutOfMemory,
BadFileDescriptor,
NotFound,
PermissionDenied,
WouldBlock,
Interrupted,
Aborted,
BrokenPipe,
InvalidInput,
Unknown,
}
struct IOFailed { code: int, requested: i64 }
struct TTYSize { rows: int, cols: int }
struct TTYRawMode { handle: u64 }
struct BufferedWriter { fd: int, buf: std::buffer::BufferU8, flush_threshold: i64 }
export type IOResult = std::result::Result(int, IOFailed);
export type IOError = IOFailed;
export type IOErrorIntResult = std::result::Result(int, IOError);
export type TTYRawModeResult = std::result::Result(TTYRawMode, IOFailed);
export type BufferedWriterResult = std::result::Result(BufferedWriter, IOFailed);
export fn read (fd: int, buf: std::arrays::ByteSlice) -> IOResult;
export fn write (fd: int, buf: std::arrays::ByteSlice) -> IOResult;
export fn write_all (fd: int, buf: std::arrays::ByteSlice) -> IOFailed?;
export fn read_to_end (fd: int, mut out: &std::buffer::BufferU8) -> IOErrorIntResult;
export fn read_stdin (buf: std::arrays::ByteSlice) -> IOResult;
export fn write_stdout (buf: std::arrays::ByteSlice) -> IOResult;
export fn write_stderr (buf: std::arrays::ByteSlice) -> IOResult;
export fn isatty (fd: int) -> bool;
export fn tty_size (fd: int) -> TTYSize?;
export fn tty_open () -> IOResult;
export fn tty_raw_mode (fd: int) -> TTYRawModeResult;
export fn puts (s: string) -> IOFailed?;
export fn print (fmt: string, ...args: std::fmt::Arg) -> PrintFailed?;
export fn println (fmt: string, ...args: std::fmt::Arg) -> PrintFailed?;
export fn eprint (fmt: string, ...args: std::fmt::Arg) -> PrintFailed?;
export fn eprintln (fmt: string, ...args: std::fmt::Arg) -> PrintFailed?;
impl BufferedWriter {
public fn init (fd: int, capacity: i64) -> BufferedWriterResult;
public fn stdout (capacity: i64) -> BufferedWriterResult;
public fn stderr (capacity: i64) -> BufferedWriterResult;
public fn set_flush_threshold (mut self: &BufferedWriter, threshold: i64) -> void;
public fn write (mut self: &BufferedWriter, bytes: std::arrays::ByteSlice) -> IOFailed?;
public fn write_string (mut self: &BufferedWriter, s: string) -> IOFailed?;
public fn write_u8 (mut self: &BufferedWriter, value: u8) -> IOFailed?;
public fn flush (mut self: &BufferedWriter) -> IOFailed?;
public fn drop (mut self: &BufferedWriter) -> void;
}
The shipped async subset lives in a sibling module:
module std::io::async;
export async fn read (fd: int, buf: std::arrays::ByteSlice) -> std::io::IOResult;
export async fn write (fd: int, buf: std::arrays::ByteSlice) -> std::io::IOResult;
export async fn read_abortable (fd: int, buf: std::arrays::ByteSlice, sig: std::abort_controller::AbortSignalBorrow?) -> std::io::IOResult;
export async fn write_abortable (fd: int, buf: std::arrays::ByteSlice, sig: std::abort_controller::AbortSignalBorrow?) -> std::io::IOResult;
Notes:
print/printlnaccept Zig-std.fmt-style format strings (see fmt) and a variable number ofstd::fmt::Argarguments (within the current compiler’s varargs limit).eprint/eprintlnare the stderr equivalents ofprint/println.IOFailed.codeis a stable stdlib error code; callers should preferIOFailed.kind().- Invalid buffer arguments report
IOErrorKind::InvalidInput. read_to_endreturnsIOErrorIntResult(Ok(total_bytes)orErr(IOFailed)), where allocation failure is reported asIOErrorKind::OutOfMemoryandIOFailed.requested.BufferedWriterbatches writes to an fd-backed byte buffer.flush()explicitly writes buffered bytes;drop()attempts to flush and then releases the backing allocation. Setcapacity == 0for direct unbuffered writes.isatty(fd)returnstruewhenfdrefers to a TTY, otherwisefalse.tty_size(fd)returnsSome(TTYSize)when the window size is available (TTY mode), otherwiseNone.tty_open()opens/dev/ttyfor interactive programs and returnsOk(fd)orErr(IOFailed).tty_raw_mode(fd)enables termios raw mode and returns aTTYRawModeguard that restores the previous state on drop.- For ergonomics,
std::fmt::Argopts into the compiler’s implicit call-argument coercion mechanism (see types). This allows passing primitive values (int/fixed-width ints,usize/size,f32/f64,bool,char,string,regexp,Region) directly when calling functions that expectArgparameters (including varargs), so you can writeprintln("hello {}", "world")without explicitArg.*wrappers. Values implementingstd::interfaces::Serialize(string)also participate in this ergonomic path: whenArgis expected, the compiler may lower the value viaserialize()and then feed the resulting string intoArg.string(...). std::strings::Stringalso satisfies ordinary borrowedstringexpectations in bindings and plainstringparameters, so explicit.as_string()is no longer required solely to call helpers that takestring.- Executable outputs import external libc symbols. On
linux/x86_64,silkautomatically adds the selected libc as aDT_NEEDEDdependency when external symbols are present (libc.so.6for glibc,libc.sofor musl), so a manual libc--neededentry is not required for typical hostedstd::iouse. stringparameters inextcalls are lowered as C-string pointers in the current backend subset (the backing bytes include a trailing NUL terminator; Silkstringlength excludes it).std::io::asyncprovides small async wrappers (read/write) on top ofstd::runtime::io::{read_async,write_async}. Onlinux/*these are backed by the hosted async runtime (io_uringwhen available,poll(2)fallback). On other targets they complete immediately by issuing a blockingread/write. Abortable variants (read_abortable/write_abortable) accept an optionalstd::abort_controller::AbortSignalBorrowand returnIOErrorKind::Abortedwhen cancelled. Note: in the Supported forms, aborts are observed before starting an I/O attempt; they do not interrupt an in-flight operation.std::io::streamprovides task-based adapters that connect POSIX/WASI file descriptors (fd) withstd::stream(ReadableStream/WritableStream):std::io::stream::pipe_fd_to_stream/pipe_fd_to_stream_abortablestd::io::stream::pipe_stream_to_fd/pipe_stream_to_fd_abortableThese adapters take ownership of thefdand close it before returning.
Example (formatted printing):
import std::io;
fn main () -> int {
std::io::println("hello {s} answer={d}", "world", 42);
return 0;
}
Example (stdin → stdout echo using unbuffered reads/writes):
import std::io;
import std::arrays;
import std::runtime::io;
import std::runtime::mem;
fn main () -> int {
let buf: u64 = std::runtime::mem::alloc(64);
if buf == 0 {
return 2;
}
while true {
let r: std::io::IOResult = std::io::read_stdin(std::arrays::ByteSlice{ ptr: buf, len: 64 });
match (r) {
std::io::IOResult::Ok(n) => {
if n == 0 {
break;
}
let w_err: std::io::IOFailed? = std::io::write_all(std::runtime::io::STDOUT_FD, std::arrays::ByteSlice{ ptr: buf, len: n as i64 });
if w_err != None {
std::runtime::mem::free(buf);
return 4;
}
},
std::io::IOResult::Err(_) => {
std::runtime::mem::free(buf);
return 3;
},
}
}
std::runtime::mem::free(buf);
return 0;
}
Scope#
std::io is responsible for:
- Standard input, output, and error streams.
- Simple printing and formatted output APIs.
- Minimal fd-based async wrappers in
std::io::async. - Buffered fd-backed output for CLI tools.
Core Interfaces#
The stdlib should standardize reader/writer interfaces:
module std::io;
export enum IOErrorKind {
// Stable error kinds (portable subset).
PermissionDenied,
NotFound,
BrokenPipe,
WouldBlock,
UnexpectedEof,
Unknown,
}
export interface Writer {
write: fn(self: &Writer, bytes: std::arrays::Slice(u8)) -> Result(int, IOErrorKind);
flush: fn(self: &Writer) -> IOErrorKind?;
}
export interface Reader {
read: fn(self: &Reader, dst: std::arrays::Slice(u8)) -> Result(int, IOErrorKind);
}
The concrete representation of interfaces will evolve with the language; the
key point is that std::fs and std::net can reuse the same I/O traits.
Convenience API#
- stdout/stderr:
print/printlnandeprint/eprintln(formatted output). - unbuffered primitives:
read_stdin,write_stdout,write_stderr. - future (design):
stdout()/stderr()/stdin()handle-returning helpers built on a stable reader/writer interface.
Considerations#
- Buffered readers and async buffered I/O wrappers.
- Broader async I/O surface beyond the shipped
std::io::asyncwrappers: - richer socket and filesystem stream adapters,
- stronger cancellation of in-flight operations,
- and
select-style waiting over mixed sources.
Source repository · Edit this page · View Markdown