std.io
std.io is console output over the print builtins, plus typed line input that reads a line through the read_line builtin and parses it. The module itself is small (four exported functions) because most I/O in dusk lives in builtins that are available everywhere without an import. This page documents both: the std.io functions and the printing, stdin, and file builtins they sit on.
@import std.ioImported names are flat: after @import std.io you call print_int and print_line with no prefix. You can also qualify a call through its module path, so std.io.print_line("hi") reaches the same function. See stdlib overview for how imports work in general.
Console printing
Section titled “Console printing”print, println, and printerr are builtins, available everywhere without an import and gated behind no paradigm.
| Builtin | Signature | Description |
|---|---|---|
print | print(...) -> void | Print to stdout, no newline. |
println | println(...) -> void | Print to stdout, appending a newline. |
printerr | printerr(...) -> void | println to stderr. |
Each handles a string, an int, a float, a bool, or a char. Build a line piece by piece with print, then close it with println.
print("score: ")print(42)println("") // ends the lineA struct can be printed if it implements the Display interface. Passing a struct with no Display impl to a print builtin is a compile error, as is printing an enum, a slice, a tuple, or a pointer. Print never emits silence for a value it cannot render. See builtins for the Display interface.
printerr writes a line to stderr, flushing stdout first so program output precedes the message even when the program aborts right after. It also accepts an error value directly, which is the usual way to report a failure before returning:
n, e := read_int()if e.exists() { printerr(e) return 1}Format strings
Section titled “Format strings”With a value argument, the first argument to print, println, or printerr is a format string whose {} holes the rest fill in order. Write {{ or }} for a literal brace. The format string is a literal expanded at compile time, so each hole prints its value by type with no runtime format parser and no allocation, and a hole count that does not match the arguments is a compile error.
@paradigm procedural
func main() -> int32 { print("score: ") print(42) println("")
name: string = "Ada" age: int64 = 36 println("{} is {}", name, age) println("{{braces}} and a hole {}", 7) return 0}Module functions
Section titled “Module functions”These are the four exported functions in std.io, reachable after @import std.io.
print_int
Section titled “print_int”func print_int(n: int64) -> voidPrints an integer and a newline. A thin wrapper over println(n).
print_line
Section titled “print_line”func print_line(s: string) -> voidPrints a string and a newline. A thin wrapper over println(s).
read_int
Section titled “read_int”func read_int() -> (int64, error)Reads one line from stdin through the read_line builtin and parses it as a base 10 integer with parse_int from std.string. The error exists at end of input or when the line is not an integer, and the value beside a real error is 0.
read_float
Section titled “read_float”func read_float() -> (float64, error)Reads one line from stdin and parses it as a float with parse_float from std.string. The error exists at end of input or when the line is not a number, and the value beside a real error is 0.0.
Both readers return a (value, error) pair, so the must-handle rule applies: the caller resolves the error through exists, check, or ignore. See error handling.
@paradigm procedural
@import std.io
func main() -> int32 { print_line("enter an int:") n, e := read_int() if e.exists() { print_line("not an int") return 0 } print_int(n * 2)
print_line("enter a float:") f, fe := read_float() if fe.exists() { print_line("not a float") return 0 } println(f + 1.0) return 0}Console input
Section titled “Console input”read_line and read_all are builtins that read from stdin, available without an import.
| Builtin | Signature | Description |
|---|---|---|
read_line | read_line() -> (string, error) | Read one line from stdin, the error marking end of input. |
read_all | read_all() -> (string, error) | Read all of stdin into one string. |
read_line reads one line, read_all reads the whole stream. For read_line the error exists at end of input, so a read loop stops when it fires, while read_all errors only on an allocation failure, since the whole of an empty stream is the empty string. A line keeps no trailing newline, and an empty line is the empty string, which is distinct from end of input. Both read from the terminal when stdin is not redirected, and from a pipe or a file when it is.
dusk has no break, so read until end of input with a done flag. The loop needs mut and while, which require @paradigm procedural. See the paradigm system.
@paradigm procedural
@import std.io
func main() -> int32 { print_line("what is your name?") name, err := read_line() if err.exists() { print_line("no input") return 0 } println("hello {}", name)
print_line("enter lines, end with ctrl-d:") mut count: int64 = 0 mut done: bool = false while !done { line, e := read_line() if e.exists() { done = true } else { print_line(line) count = count + 1 } } print_int(count) return 0}File I/O
Section titled “File I/O”read_file and write_file are builtins, so they are available everywhere without an import, the same way print is.
| Builtin | Signature | Description |
|---|---|---|
read_file | read_file(path: string) -> (string, error) | Read the whole file into a heap string. |
write_file | write_file(path: string, contents: string) -> error | Write the string to the file, truncating it. |
read_file returns a (string, error) pair and write_file returns an error, so the must-handle rule applies and a caller resolves the failure through exists, check, or ignore. A failed read hands back the empty string and an error that exists.
The string read_file returns lives on the heap and is owned by the caller, so free it with free once you are done with it. It frees through the generational heap like any other allocation. See memory.
@paradigm procedural
@import std.io@import std.string
func main() -> int32 { werr := write_file("/tmp/dusk_io_example.tmp", "persisted") werr.ignore() s, rerr := read_file("/tmp/dusk_io_example.tmp") rerr.ignore() print_line(s) print_int(str_len(s)) free(s) return 0}See also
Section titled “See also”- std.string:
parse_int,parse_int_radix, andparse_float, the parsers behindread_intandread_float. - Builtins: the full builtin table, including the
Displayinterface for printing structs. - Error handling: the must-handle rule and the
exists,check, andignoremethods.