- Lab
- Core Tech

Practice: Using Metatables and Metamethods in Lua
In this Code Lab, you’ll learn how to use metatables and metamethods to implement flexible, object-like behavior in Lua. By extending basic tables with custom operations and intuitive interfaces, you’ll gain a deeper understanding of how Lua supports certain programming patterns without relying on a traditional class system.

Path Info
Table of Contents
-
Challenge
Introduction
In this Code Lab, you'll explore how Lua's metatables and metamethods can enhance plain tables with custom behaviors. Starting from a basic inventory item represented by a table, you'll gradually introduce metamethods to make your items printable, comparable, and even mathematically combinable. This pattern enables object-oriented designs for Lua tables.
Scenario
This lab begins with a plain Lua table representing a material item in a simple inventory tracking system. Initially, each item behaves like a generic key-value table, with no logic or structure beyond its raw data.
By introducing metatables and key metamethods like
__tostring
,__eq
, and__add
, you'll give these items reusable behavior—making them easier to work with, compare, and display consistently. The final result is a lightweight, OOP-like system built entirely with Lua’s native tools.
Additional Info
There is a
solutions
directory where you can compare your implementation. As you finish defining your metatable and metamethods, you can test them by runninglua test_inventory.lua
in the Terminal. -
Challenge
Basics of Metatables and Metamethods
How Metatables Work
In Lua, all tables can have a metatable—a special table that defines how they behave in certain situations (like printing, arithmetic, indexing, etc.). Metatables can contain many different metamethods, which are keys that Lua checks for specific operations. Some of these include:
__tostring
→ called when usingprint()
__eq
→ called when using==
__add
→ called when using+
__index
→ used to redirect method lookups when a key is missing
To attach a metatable to a table, use
setmetatable()
:local obj = {} setmetatable(obj, metatable)
Important: Although you can technically set custom methods as metamethods, usually it is best practice to reserve metamethods for built-in Lua operations that may not be compatible with plain tables.
Take a look at
inventory.lua
. In this file, you will find the definition for anInventory
table along with a constructor and some methods belonging to this table. There is a metatableInventory.mt
defined, but it does not yet have any metamethods defined nor is the table attached to any table instances. You should set that up now.
Task
Implement a metatable in the
Inventory
constructor to allow instances to inherit methods from theInventory
prototype via the__index
metamethod.Instructions
- Within the
Inventory.new()
constructor method, add asetmetatable(item, Inventory.mt)
. This will attach the item created using the constructor to the metatable. - Within the metatable(
Inventory.mt
), define an__index
metamethod and set it toInventory
like so__index = Inventory
.
Solution
-- Constructor function Inventory.new(name, quantity, unit_price) local item = { name = name, quantity = quantity, unit_price = unit_price } -- Add a metamethod and attach a table return setmetatable(item, Inventory.mt) end -- Metatable with metamethods Inventory.mt = { __index = Inventory }
In doing so, you have made sure that all items that are instances of
Inventory
are attached to a metatable. Defining the__index
metamethod allows any item to inherit methods fromInventory
. In other words, an item such aslocal bolts = Inventory.new("Bolt", 50, 0.25)
would now have access torestock()
andvalue()
thatInventory
already implements.
Next, add metamethods to support
__tostring()
,__eq()
, and__add()
.Task
First, implement the
__tostring()
metamethod.Instructions
- In the metatable, add a
__tostring
method. It should takeself
as a parameter. - You should return a
string.format()
with the following string:"%s x%d (Unit: $%.2f, Total: $%.2f)"
. - You should then provide
self.name
,self.quantity
,self.unit_price
, andself:value()
for this formatted string.
Solution
__tostring = function(self) return string.format( "%s x%d (Unit: $%.2f, Total: $%.2f)", self.name, self.quantity, self.unit_price, self:value() ) end,
When using
print(item)
on a plain table, Lua will just print out its memory address (ie. 0x77777777), but this metamethod changes it so thatprint(item)
will instead print out the items details.
Task
Next, implement the
__eq()
metamethod.Instructions
- In the metatable, add a
__eq
method. It should take the variablesa
andb
as parameters. - You should return an equality check by checking if the
name
,quantity
, andunit_price
ofa
andb
are equal using==
.
Solution
__eq = function(a, b) return a.name == b.name and a.quantity == b.quantity and a.unit_price == b.unit_price end,
This enables Lua to perform
item1 == item2
when it normally wouldn't support this between two tables.
Task
Lastly, implement the
__add()
metamethod.Instructions
- In the metatable, add a
__add
method. It should take the variablesa
andb
as parameters. - Check if
name
andunit_price
are the same fora
andb
. If not, throw anerror("Cannot combine different inventory items")
and end the function. - Otherwise, return an
Inventory.new()
passing in thename
,a.quantity + b.quantity
, andunit_price
as parameters. You can use eithera
orb
for thename
andunit_price
.
Solution
__add = function(a, b) if a.name ~= b.name or a.unit_price ~= b.unit_price then error("Cannot combine different inventory items") end return Inventory.new( a.name, a.quantity + b.quantity, a.unit_price ) end
This allows Lua to perform
item1 + item2
between two tables.
After you've implemented the metatable and metamethods for
Inventory
items, you can take a look attest_inventory.lua
. There are commented sections that depict how each metamethod is invoked for each line of code. Test run them withlua test_inventory.lua
.You should see something like:
Spoiler
Hammer x100 (Unit: $15.00, Total: $1500.00) Items are different Bolt x300 (Unit: $0.25, Total: $75.00)
-
Challenge
Conclusion
Wrapping Up
Without metatables, you'd manually repeat behavior like string formatting, comparison logic, arithmetic logic, and more each time you need to apply them. You’d also lack clear structure, especially when mixing multiple items.
By incorporating metatables and metamethods, you empower basic tables in Lua to act more like objects while keeping things simple and performant. This is especially helpful when you want to simulate object-oriented behavior in Lua without full-blown class systems.
You can learn more about all the other metamethods and uses for metatables through the official Lua documentation.
What's a lab?
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Provided environment for hands-on practice
We will provide the credentials and environment necessary for you to practice right within your browser.
Guided walkthrough
Follow along with the author’s guided walkthrough and build something new in your provided environment!
Did you know?
On average, you retain 75% more of your learning if you get time for practice.