io
import io from "io";
io.println("Hello, {name}!");
io.eprintln("warning: no config file");
let line: ?string = io.read_line() or { return 1; }; // null at the end of inputio is the console, the streams under files, pipes and sockets, and
IoError, the error that io, fs, os, process and net all report. It is a capability module: it
touches the outside world.
Printing
| Function | Writes to |
|---|---|
print(s: []const u8) | stdout |
println(s: []const u8) | stdout, then a newline |
eprint(s: []const u8) | stderr |
eprintln(s: []const u8) | stderr, then a newline |
Any string, literal or []const u8 works, and interpolation builds the
text: io.println("{count} files, {size} bytes"). A C string from a C
library goes through cstr.as_view(p) first.
Printing is not buffered: every call goes straight to the console, so
nothing is lost when main returns or the program traps. For many small
writes, use a BufWriter (below).
Reading lines
while (true) {
let line: ?string = io.read_line() or { return 1; };
if (line == null) { break; } // end of input
let text: string = line!;
io.println("got: {text}");
}read_line() -> ?string or IoError returns the next line of stdin without
its line ending ("\n" or "\r\n"), and null at the end of the input.
The last line doesn’t need a newline. It reads one byte at a time, so it
never takes more of stdin than the line it returns.
Streams
A Reader reads from, and a Writer writes to, a file descriptor: the
standard streams or an open file (fs.File.reader(), fs.File.writer());
or a socket, a TCP connection’s (net.TcpStream.reader(), writer());
or a hook, a function with a context, which is how a TLS connection
(net.TlsStream.reader(), writer()) is a stream.
pub enum StreamKind : u8 { Descriptor = 0; Socket = 1; Hook = 2; }
pub struct Reader { fd: i64; kind: StreamKind = StreamKind.Descriptor;
ctx: *u8 = null; read_fn: fn(*u8, *u8, i64) -> i64 = null; }
pub struct Writer { fd: i64; kind: StreamKind = StreamKind.Descriptor;
ctx: *u8 = null; write_fn: fn(*u8, *const u8, i64) -> i64 = null; }
pub fn hook_reader(ctx: *u8, read_fn: fn(*u8, *u8, i64) -> i64) -> Reader;
pub fn hook_writer(ctx: *u8, write_fn: fn(*u8, *const u8, i64) -> i64) -> Writer;kind says which: Reader { fd: f.fd; } is a descriptor, a socket’s
views carry StreamKind.Socket so their reads and writes go to the socket
(Windows sockets are not file descriptors), and a hook’s call its function
with its context: read_fn(ctx, buf, len) returns the bytes read (0 at
the end), write_fn(ctx, buf, len) the bytes taken, either a negative
IoError value (-(e as i64)) on failure. Everything below, the
buffered types included, works on all three. A Reader or Writer
doesn’t own what it reads or writes: whoever opened it closes it.
// A Writer that counts what goes through it
struct Counter { n: i64; }
fn count(ctx: *u8, buf: *const u8, len: i64) -> i64 {
var c: *Counter = ctx as *Counter;
c.n = c.n + len;
return len;
}
var c: Counter = Counter { n: 0; };
let w: io.Writer = io.hook_writer(&c as *u8, count);
w.write_all("hello") or {}; // c.n is 5| Function | Does |
|---|---|
stdin() -> Reader, stdout() -> Writer, stderr() -> Writer | The standard streams |
r.read(buf: []u8) -> i64 or IoError | Read up to buf.len bytes; 0 at the end |
r.read_all() -> string or IoError | Everything up to the end |
r.read_line() -> ?string or IoError | The next line, null at the end |
w.write(data) -> i64 or IoError | Write some of data; returns how many bytes |
w.write_all(data) -> void or IoError | Write all of data |
w.flush() -> void or IoError | Nothing to do for a Writer; there for symmetry |
write may take fewer bytes than you give it (a pipe, a signal); use
write_all unless you handle that yourself.
Buffering
A BufWriter collects small writes and passes them on a buffer at a time.
A BufReader reads a buffer at a time and hands out bytes and lines from
it.
var out: io.BufWriter = io.buf_writer(io.stdout());
for (i in 0..1000) {
out.write_all("line {i}\n") or { return 1; };
}
out.close() or { return 1; }; // flushes, then frees the buffer| Function | Does |
|---|---|
buf_writer(w: Writer) -> BufWriter | An 8 KiB (BUF_SIZE) buffer over w |
buf_writer_sized(w, cap) -> BufWriter | A buffer of cap bytes |
b.write_all(data), b.write(data) | Buffer data, flushing when it doesn’t fit |
b.buffered() -> i64 | Bytes waiting in the buffer |
b.flush() -> void or IoError | Write what is buffered |
b.close() -> void or IoError | Flush and free the buffer |
buf_reader(r: Reader) -> BufReader, buf_reader_sized(r, cap) | A buffered reader |
b.read(buf), b.read_line(), b.read_all() | As the Reader’s, a buffer at a time |
b.close() | Free the buffer |
Buffered bytes are only written on flush or close: if the program ends
first, they are lost. Closing a BufWriter or BufReader doesn’t close
the file underneath. A capacity below 1, and any use after close, stop
the program with a trap.
IoError
let text: string = fs.read_file("config.ini") or {
io.eprintln("cannot read config.ini: {err}"); // "cannot read config.ini: not found"
return 1;
};| Variant | Description |
|---|---|
NotFound | not found |
PermissionDenied | permission denied |
AlreadyExists | already exists |
NotADirectory | not a directory |
IsADirectory | is a directory |
DirectoryNotEmpty | directory not empty |
InvalidInput | invalid input |
UnexpectedEof | unexpected end of file |
BrokenPipe | broken pipe |
Interrupted | interrupted |
NoSpace | no space left on device |
Unsupported | not supported on this platform |
ConnectionRefused | connection refused |
ConnectionReset | connection reset by peer |
ConnectionAborted | connection aborted |
NotConnected | not connected |
AddrInUse | address in use |
AddrNotAvailable | address not available |
NetworkUnreachable | network unreachable |
HostUnreachable | host unreachable |
TimedOut | timed out |
WouldBlock | operation would block |
HostNotFound | host not found |
Other | I/O error |
The same failure is the same variant on every platform: opening a missing
file is NotFound on Windows, Linux and macOS alike. The variants from
ConnectionRefused to HostNotFound come from sockets (net).
Deprecated
print_i64, print_bool and parse_int still work and will be removed in
a later release. Use interpolation, io.println("{n}"), and
conv.parse_i64, which reports bad input instead of stopping at it.
Platforms
Everything works on Windows, Linux and macOS. On Uusi the console and
files work (see fs); errors of the kernel’s own files are all Other
for now.
Last modified: