I/O Operations in V (Vlang)
Learn console input, formatted output, and file reading and writing in V, with Result-based error handling and Docker-ready examples
Input and output are where a program meets the outside world – and the outside world is unreliable. Files go missing, permissions fail, and users type unexpected things. How a language handles that unreliability says a lot about its design, and V’s answer is characteristically opinionated: there are no exceptions. Every I/O function that can fail returns a Result type, and the compiler forces you to handle it with an or block at the call site.
V splits its I/O between two layers. Console output (print, println, eprintln) is built into the language – no imports needed, as you saw in the Hello World tutorial. Everything else – reading from standard input, and reading and writing files – lives in the os module from the standard library.
In this tutorial you’ll learn formatted console output, reading user input from stdin, and the two styles of file I/O V offers: convenient one-shot functions like os.read_file, and explicit file handles for streaming and appending. Along the way you’ll see how V’s mandatory error handling makes I/O failures impossible to silently ignore.
Formatted Console Output
Hello World introduced println. V also provides print (no trailing newline) and eprintln (writes to standard error), plus printf-style format specifiers inside string interpolation.
Create a file named io_output.v:
| |
The format specifier syntax follows the familiar printf pattern: ${value:[width][.precision][type]}. Here :.2f rounds a float to two decimal places, and :5 right-aligns a value in a field five characters wide. Because the specifiers live inside interpolation, there is no separate printf function to learn – the same ${} syntax you already use for plain interpolation handles formatting too.
eprintln matters more than it might look: sending diagnostics to standard error keeps them out of your program’s real output, so pipelines like v run io_output.v > results.txt capture only the data.
Reading Input from the Terminal
Reading from stdin uses os.input, which prints a prompt and returns the line the user typed (without the trailing newline). Since it comes from the os module, this is our first example that needs an import.
Create a file named io_input.v:
| |
Two things to notice. First, os.input returns a string – always. V’s static, strong typing means there is no implicit conversion, so turning the response into a number requires an explicit call like .int() or .f64(). Second, .int() never throws: if the string isn’t a valid number it simply returns 0, so validate input when zero is a meaningful value in your program.
File I/O with Result Types
File operations are where V’s error-handling philosophy becomes unavoidable. Functions like os.read_file are declared with a ! return type, which means they can fail – and the compiler will refuse to compile your program unless every call site handles that failure with an or block, propagates it with !, or converts it deliberately.
This example writes a file, reads it back two ways, appends to it with a file handle, and cleans up.
Create a file named io_files.v:
| |
Inside every or block, the special variable err holds the error value – no declaration needed. The block must end by producing a fallback value, returning, panicking, or otherwise leaving the function, so an I/O failure can never fall through unnoticed.
The example shows both of V’s file I/O styles. The one-shot functions (os.write_file, os.read_file, os.read_lines) open, operate, and close in a single call – ideal for small files. The handle style (os.open_append returning an os.File) keeps the file open across multiple writes, which is what you want for logs or large streamed output. Note that the handle must be declared mut, because writing mutates the file object – immutability by default applies to file handles too. Related helpers in the same module include os.open (read), os.create (write from scratch), and os.exists to test for a file without opening it.
Running with Docker
| |
The interactive example needs Docker’s -i flag so your keyboard input reaches the program inside the container.
Expected Output
Running io_output.v prints (the warning line goes to stderr, so it disappears if you redirect stdout):
Loading... done
pi to two decimals: 3.14
count in a 5-wide field: [ 42]
warning: this line goes to stderr
Running io_input.v and typing Ada and 30 at the prompts:
What is your name? Ada
Hello, Ada!
How old are you? 30
Next year you will be 31.
Running io_files.v:
first line
second line
line count: 2
1: first line
2: second line
3: third line
Key Concepts
- Console output is built in; everything else is in
os–print,println, andeprintlnneed no import, while stdin and file operations requireimport os - Format specifiers live inside interpolation –
${pi:.2f}and${count:5}give printf-style control without a separate printf function eprintlnwrites to standard error – keep diagnostics out of stdout so redirected or piped output stays cleanos.inputreturns a string, always – convert explicitly with.int()or.f64(); V’s strong typing never converts for you- I/O failures cannot be ignored – fallible functions return Result types (
!), and the compiler requires anorblock, propagation, or panic at every call site erris available inside everyorblock – it carries the error value with no declaration needed- Choose one-shot functions or file handles –
os.read_file/os.write_filefor small files in a single call;os.open_appendand friends for streaming and repeated writes - File handles must be
mut– writing mutates the handle, and V’s immutability-by-default applies to files like everything else
Running Today
All examples can be run using Docker:
docker pull thevlang/vlang:alpine
Comments
Loading comments...
Leave a Comment