print()
print(...) print() is Lua’s built-in function for writing values to standard output. It converts each argument to a string, joins them with tab characters, and appends a newline. It is the most direct way to inspect values during development — no imports, no libraries, no configuration required.
Signature
print(...)
The ... means it accepts any number of arguments, including none. You can call print() to output a blank line, pass a single value for quick inspection, or dump several variables at once — each one gets converted to a string automatically.
Return Value
Unlike most Lua standard library functions that return useful data or nil on failure, print() always returns nil. It exists purely for its side effect — writing to stdout. If you need to capture formatted output as a string, use string.format() and store the result instead.
Basic Usage
print("Hello, world!") -- Hello, world!
print(42) -- 42
print(true, false, nil) -- true false nil
With no arguments, print() outputs a blank line — it still appends the newline character, so what you actually get is an empty line followed by a line break. This is occasionally useful for visually separating sections of debug output, though calling print("\n") is more explicit about the intent:
print() -- (blank line)
Multiple Arguments
When you pass more than one value to print(), Lua converts each argument to a string using tostring() and joins the results with a single tab character (\t). This tab separation is fixed — you can’t change it to spaces, commas, or any other delimiter. If you need custom formatting between values, build the string yourself with string.format() or concatenation and pass a single argument to print():
name = "Alice"
age = 32
print("Name:", name, "Age:", age)
-- Name: Alice Age: 32
Lua’s print converts values to strings using its own internal conversion rules:
- Strings: printed as-is
- Numbers: converted to their decimal representation
- Booleans:
"true"or"false" nil: printed as the word"nil"- Tables: printed as
"table: 0x..."(memory address — not recursively expanded)
These built-in conversion rules are fast and predictable, but they leave you with no control over precision, alignment, or table contents. When you need more than the defaults, you reach for explicit string conversion functions.
Converting to string explicitly
For more control over formatting, use tostring():
value = 3.14159
print(tostring(value)) -- 3.14159
print(string.format("%.2f", value)) -- 3.14
-- tostring on a table gives the same address-style output
print(tostring({a = 1})) -- table: 0x7f8a3c402890
tostring() is called implicitly by print() on each argument, so print(x) and print(tostring(x)) produce identical output — except that tostring() can be customised by defining the __tostring metamethod on a table.
Customising table output with __tostring
Define the __tostring metamethod to control how a table is printed:
Point = {}
Point.__index = Point
function Point.new(x, y)
return setmetatable({x = x, y = y}, Point)
end
function Point:__tostring()
return string.format("Point(%.1f, %.1f)", self.x, self.y)
end
p = Point.new(2.5, 4.0)
print(p) -- Point(2.5, 4.0)
Without __tostring, print(p) would output something like table: 0x7f8a3c402890 — a raw memory address that’s different on every run and reveals nothing about the data. Defining __tostring turns opaque debug output into something you can actually read.
Inspecting Variables with print
The most common use of print() is quick-and-dirty debugging — drop a print call into a function to see what values are flowing through it, then remove it when you’re done. No breakpoints, no debugger setup, just immediate feedback. The example below shows a function instrumented with three print calls that trace the input, an error condition, and the result:
function divide(a, b)
print("divide called with", a, b)
if b == 0 then
print("ERROR: division by zero")
return nil
end
print("result:", a / b)
return a / b
end
divide(10, 2)
-- divide called with 10 2
-- result: 5
Limitations
print() is not suitable for production output:
- No formatting control — tabs and newline are fixed
- No file output — always writes to stdout
- No structured data — tables print as addresses
- Not thread-safe in coroutine-heavy programs (output can interleave)
For formatted output, use string.format(). For file output, use io.write() or file:write().
io.write as an Alternative
io.write() is similar to print() but offers more control:
io.write("Count: ") -- no newline
io.write(42, "\n") -- with newline
-- Formatted output
io.write(string.format("Pi to 3 decimals: %.3f\n", math.pi))
Key differences from print():
io.write()does not add a newline automaticallyio.write()does not add tab separators between argumentsio.write()returnsnilon error (like most io functions), not the values
See Also
- /reference/core-functions/ref-tostring/ — convert any Lua value to its string representation
- /reference/core-functions/ref-pcall/ — protected calls for handling errors during debugging
- /reference/core-functions/ref-type/ — inspect the type of a value before printing