I/O Operations in Mojo
Learn console output, keyboard input, and file reading and writing in Mojo with practical Docker-ready examples
Input and output are how a program talks to the outside world—printing results, reading what a user types, and persisting data to disk. Because Mojo is designed as a Python superset, its I/O surface will feel immediately familiar: print(), input(), and open() all behave much as they do in Python.
The difference is what happens underneath. Mojo compiles to native machine code through MLIR, so these operations run without a Python interpreter in the loop. The print() function accepts the same sep and end keywords you know from Python, open() returns a file handle that works as a context manager, and strings interoperate cleanly with Mojo’s typed values through explicit String() conversions.
In this tutorial you will learn how to produce flexible console output, read keyboard input from standard input, and write and read files on disk. All examples use def functions, which give you Python’s dynamic, exception-raising behavior—the most convenient choice when doing I/O.
Console Output
You already saw print() in Hello World. Here we go further: multiple values on one line, custom separators, and controlling the trailing newline.
Create a file named console_output.mojo:
def main():
# A standard line of output
print("Standard output line")
# Several values on one line (default separator is a space)
print("Values:", 42, 3.14, True)
# A custom separator with the sep keyword
print("2026", "07", "08", sep="-")
# Suppress the trailing newline with the end keyword
print("Loading", end="")
print("...", end="")
print(" done")
# Build a row with a custom separator
print("Name", "Score", sep=" | ")
print("Ada", 95, sep=" | ")
The print() function accepts any number of Writable values—strings, integers, floats, and booleans all work together in a single call. The sep keyword controls what goes between values (a space by default), while end controls what goes after the final value (a newline by default). Setting end="" lets you assemble a line from several print() calls.
Reading Keyboard Input
The built-in input() function reads a line of text from standard input and returns it as a String. Because input can fail (for example, at end-of-file), it can raise—and def functions handle raising automatically.
Create a file named read_input.mojo:
def main():
print("Enter your name:")
var name = input()
print("Hello,", name)
print("Enter the year you were born:")
var year_text = input()
# input() always returns a String; convert it to an Int with atol()
var birth_year = atol(year_text)
print("In 2026 you will turn", 2026 - birth_year)
Note that input() returns a String, so to do arithmetic you must convert the text to a number. Here atol() (ASCII-to-long) parses the string as a base-10 integer. If the user types something that isn’t a number, atol() raises an error—another reason def functions, which propagate exceptions, are convenient for interactive programs.
Writing and Reading Files
Mojo’s open() function returns a file handle. Used with a with statement, the file is automatically closed when the block ends—even if an error occurs. The mode string works like Python’s: "w" creates or overwrites a file for writing, and "r" opens it for reading.
Create a file named file_io.mojo:
def main():
# Write text to a new file ("w" creates or overwrites it)
with open("greetings.txt", "w") as f:
f.write("Hello from Mojo!\n")
f.write("Line two of the file.\n")
print("File written successfully.")
# Read the entire file back into a String
with open("greetings.txt", "r") as f:
var contents = f.read()
print("File contents:")
print(contents, end="")
The write() method appends text to the file at the current position; call it repeatedly to build up content. The read() method returns the whole file as a single String. Because the file already ends with a newline, we pass end="" to the final print() so we don’t add a blank line.
Formatted Output
Mojo builds formatted strings through concatenation and explicit conversions. The String() constructor turns integers, floats, and booleans into text, and the + operator joins strings together.
Create a file named formatted_output.mojo:
def main():
var language: String = "Mojo"
var year = 2026
var released = True
# Concatenate strings; convert numbers and booleans with String()
print(language + " was compiled in the year " + String(year))
print("Released: " + String(released))
# Print columns with a custom separator
print("CPU", "GPU", "TPU", sep=" | ")
# A separator line and a footer
print("--------------------")
print("End of report")
Explicit String() conversion is a small but important habit in Mojo. In a strict fn function the compiler would reject concatenating a String with a raw Int, forcing you to convert. Doing it here in a def keeps the two styles consistent and makes the code portable to stricter contexts.
Running with Docker
| |
The read_input.mojo example reads from standard input, so run it with the interactive -i flag so Docker forwards your keystrokes to the program:
| |
Expected Output
Running console_output.mojo:
Standard output line
Values: 42 3.14 True
2026-07-08
Loading... done
Name | Score
Ada | 95
Running file_io.mojo:
File written successfully.
File contents:
Hello from Mojo!
Line two of the file.
Running formatted_output.mojo:
Mojo was compiled in the year 2026
Released: True
CPU | GPU | TPU
--------------------
End of report
Running read_input.mojo is interactive. A sample session (user input shown after each prompt) looks like this:
Enter your name:
Ada
Hello, Ada
Enter the year you were born:
1815
In 2026 you will turn 211
Key Concepts
print()mirrors Python — it accepts any number ofWritablevalues and supports thesepandendkeywords to control separators and the trailing newline.input()returns aString— always convert to a number (for example withatol()) before doing arithmetic on typed input.open()withwith— file handles work as context managers, so files are closed automatically when the block ends, even on error.- File modes match Python —
"w"overwrites,"r"reads;write()adds text andread()returns the whole file as aString. - I/O can raise —
deffunctions propagate exceptions automatically, which is why they’re convenient for input and file work; a strictfnwould require an explicitraisesannotation. - Explicit
String()conversion — turning integers, floats, and booleans into text keeps concatenation clean and makes code portable to stricterfncontexts.
Running Today
All examples can be run using Docker:
docker pull codearchaeology/mojo:latest
Comments
Loading comments...
Leave a Comment