Featured resource
Tech Upskilling Playbook 2025
Tech Upskilling Playbook

Build future-ready tech teams and hit key business milestones with seven proven plays from industry leaders.

Learn more
  • Labs icon Lab
  • Core Tech
Labs

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.

Labs

Path Info

Level
Clock icon Intermediate
Duration
Clock icon 10m
Last updated
Clock icon Jun 11, 2025

Contact sales

By filling out this form and clicking submit, you acknowledge our privacy policy.

Table of Contents

  1. 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 running lua test_inventory.lua in the Terminal.

  2. 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 using print()
    • __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 an Inventory table along with a constructor and some methods belonging to this table. There is a metatable Inventory.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 the Inventory prototype via the __index metamethod.

    Instructions
    1. Within the Inventory.new() constructor method, add a setmetatable(item, Inventory.mt). This will attach the item created using the constructor to the metatable.
    2. Within the metatable(Inventory.mt), define an __index metamethod and set it to Inventory 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 from Inventory. In other words, an item such as local bolts = Inventory.new("Bolt", 50, 0.25) would now have access to restock() and value() that Inventory already implements.


    Next, add metamethods to support __tostring(), __eq(), and __add().

    Task

    First, implement the __tostring() metamethod.

    Instructions
    1. In the metatable, add a __tostring method. It should take self as a parameter.
    2. You should return a string.format() with the following string: "%s x%d (Unit: $%.2f, Total: $%.2f)".
    3. You should then provide self.name, self.quantity, self.unit_price, and self: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 that print(item) will instead print out the items details.


    Task

    Next, implement the __eq() metamethod.

    Instructions
    1. In the metatable, add a __eq method. It should take the variables a and b as parameters.
    2. You should return an equality check by checking if the name, quantity, and unit_price of a and b 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
    1. In the metatable, add a __add method. It should take the variables a and b as parameters.
    2. Check if name and unit_price are the same for a and b. If not, throw an error("Cannot combine different inventory items") and end the function.
    3. Otherwise, return an Inventory.new() passing in the name, a.quantity + b.quantity, and unit_price as parameters. You can use either a or b for the name and unit_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 at test_inventory.lua. There are commented sections that depict how each metamethod is invoked for each line of code. Test run them with lua 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)
    
  3. 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.

George is a Pluralsight Author working on content for Hands-On Experiences. He is experienced in the Python, JavaScript, Java, and most recently Rust domains.

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.