Skip to main content
    Courses/Lua/Variables and Data Types

    Lesson 2 • Beginner

    Variables and Data Types

    By the end of this lesson you'll store text, numbers, and true/false values in Lua, know its handful of types, join strings with .., measure them with #, convert between types on purpose, and understand why local should be your default — the bedrock of every Lua script.

    What You'll Learn

    • Declare variables and tell local apart from global (and why local wins)
    • Name Lua's 8 types and use the 5 you'll meet daily
    • Understand dynamic typing — the value has a type, the name doesn't
    • Join text with the .. operator and avoid the + trap
    • Measure length with #, and convert with tostring / tonumber
    • Recognise nil and handle 'missing value' safely

    💡 Real-World Analogy

    A global variable is like writing on a shared whiteboard in a busy office — anyone in any room can read it, and anyone can wipe it or scribble over it. A local variable is a sticky note inside your own notebook: only you, in this room, can see it, and it's thrown away when you leave. Most bugs in beginner Lua come from scribbling on the shared whiteboard by accident, so you'll reach for the notebook (local) almost every time.

    1. local vs Global Variables

    In Lua you make a variable just by assigning to a name: score = 100. But here's the surprise that catches everyone — that variable is global by default. To keep it tidy and safe you put local in front: local score = 100. A global is visible to your whole program and every library; a local only lives inside its current block. Read this worked example, run it, and watch the local vanish outside its block.

    Worked example: local vs global scope
    -- A variable is a NAMED box that holds one value.
    -- In Lua you create one by just assigning to a name:
    score = 100          -- this is a GLOBAL variable (avoid this!)
    local lives = 3      -- this is a LOCAL variable (almost always what you want)
    
    -- Why does it matter? A global is visible to EVERY part of your program,
    -- so a typo or a clash can quietly break code far away. A local only
    -- lives inside the block it was declared in, so it stays out of the way.
    
    print(score)         -- prints: 100
    print(lives)         -- prints: 3
    
    -- Locals disappear at the end of their block. 'do ... end' is just a block.
    do
      local secret = 42  -- only exists between 'do' and 'end'
      print(secret)      -- prints: 42
    end
    print(secret)        -- prints: nil   (secret is gone — it was local to the block)
    Lua runs outside the browser — try this code atonecompiler.com/luaor locally with the lua command.

    The #1 Lua Gotcha

    Because a bare assignment creates a global, a typo silently makes a new variable instead of erroring. Write socre = 100 when you meant score, and Lua happily creates socre — your real value never changes and nothing warns you. Defaulting to local turns this whole class of bug into something you can actually spot.

    2. The Eight Types (and Dynamic Typing)

    Lua keeps things small: there are exactly 8 typesnil, boolean, number, string, table, function, userdata, and thread. Day to day you'll use the first five. Note that one number type covers both whole numbers and decimals. Lua is dynamically typed: a variable name carries no type at all — only the value inside it does — so the same name can hold a number now and a string a moment later. Use type(x) any time you want to ask what a value is.

    Worked example: the core types + type()
    -- Lua has 8 types. As a beginner you mostly meet the first five.
    -- 'type(x)' tells you the type of any value as a string.
    
    local nothing  = nil          -- nil    = "no value / does not exist"
    local ok       = true         -- boolean = true or false
    local age      = 25           -- number  = ints and decimals (one type)
    local pi       = 3.14159      -- number  too
    local name     = "Ada"        -- string  = text, in "double" or 'single' quotes
    local scores   = {10, 20, 30} -- table   = Lua's one container type
    
    print(type(nothing))  -- prints: nil
    print(type(ok))       -- prints: boolean
    print(type(age))      -- prints: number
    print(type(name))     -- prints: string
    print(type(scores))   -- prints: table
    
    -- DYNAMIC TYPING: a variable has no fixed type — only the VALUE does.
    -- The same name can hold a number now and a string later.
    local x = 10
    print(type(x))        -- prints: number
    x = "now I'm text"
    print(type(x))        -- prints: string
    Lua runs outside the browser — try this code atonecompiler.com/luaor locally with the lua command.

    📊 Lua's 8 Types at a Glance

    TypeHoldsExample
    nilNo value / absencelocal x = nil
    booleantrue or falselocal ok = true
    numberInts and decimalslocal n = 3.14
    stringTextlocal s = "hi"
    tableLua's only containerlocal t = {1, 2}
    functionReusable behaviourlocal f = print
    userdataData from C code(advanced)
    threadA coroutine(advanced)

    The greyed rows (function, userdata, thread) come later — you'll meet function and table properly in the next lesson.

    Your turn. The program below is almost finished — fill in the three blanks marked ___ using the hints, then run it and compare with the expected output.

    🎯 Your turn: declare locals and concatenate
    -- 🎯 YOUR TURN — replace each ___ then run the code.
    
    -- 1) Make a LOCAL string called "player" set to any name
    local player = ___        -- 👉 text in "quotes", e.g. "Sam"
    
    -- 2) Make a LOCAL number called "level" set to a whole number
    local level = ___         -- 👉 a number, no quotes, e.g. 5
    
    -- 3) Build a message by concatenating with  ..  (two dots)
    --    Hint: "text" .. variable .. "more text"
    local message = "Player " .. ___ .. " is on level " .. ___   -- 👉 use player, then level
    
    print(message)
    
    -- ✅ Expected output:
    -- Player Sam is on level 5
    Lua runs outside the browser — try this code atonecompiler.com/luaor locally with the lua command.

    3. Strings: Joining, Length, and Converting

    To glue text together you use the concatenation operator .. (two dots) — not +, which is only for number maths. To find how long a string is, put the length operator # in front of it: #"Lua" is 3. When you need to move between text and numbers deliberately, tostring(value) turns anything into text and tonumber(text) turns text into a number (or gives nil if it isn't one). Numbers used inside .. convert to text for you automatically.

    Worked example: .. concatenation, # length, conversions
    -- Join (concatenate) strings with two dots: ..
    local first = "Grace"
    local last  = "Hopper"
    local full  = first .. " " .. last
    print(full)              -- prints: Grace Hopper
    
    -- '..' joins; it is NOT '+'. Using '+' on text is an error (see below).
    -- Lua will auto-convert a NUMBER to text inside '..':
    local lvl = 7
    print("Level " .. lvl)   -- prints: Level 7
    
    -- # is the LENGTH operator. On a string it counts the bytes (characters).
    print(#full)             -- prints: 12   (G-r-a-c-e-space-H-o-p-p-e-r)
    print(#"Lua")            -- prints: 3
    
    -- Convert ON PURPOSE with tostring() and tonumber():
    local n = 42
    print(tostring(n) .. "!")        -- prints: 42!     (number -> string)
    print(tonumber("3.5") + 0.5)     -- prints: 4.0     (string -> number)
    print(tonumber("hello"))         -- prints: nil     (not a number -> nil)
    Lua runs outside the browser — try this code atonecompiler.com/luaor locally with the lua command.

    Now you try. Text typed by a user always arrives as a string, so before doing maths you convert it. Fill in the two blanks:

    🎯 Your turn: convert text and measure length
    -- 🎯 YOUR TURN — input from a user always arrives as TEXT.
    local quantityText = "3"      -- this is a STRING, not a number
    local word = "dragon"
    
    -- 1) Turn quantityText into a real number with tonumber(...)
    local quantity = ___          -- 👉 tonumber(quantityText)
    
    -- 2) Get the length of "word" with the # operator
    local letters = ___           -- 👉 #word
    
    -- 3) Concatenate: numbers auto-convert, so just join with ..
    print("You have " .. quantity .. " items")
    print("The word has " .. letters .. " letters")
    
    -- ✅ Expected output:
    -- You have 3 items
    -- The word has 6 letters
    Lua runs outside the browser — try this code atonecompiler.com/luaor locally with the lua command.

    Pro Tips

    • 💡 Default to local. Put it on every variable unless you genuinely need a global. Your future self will thank you.
    • 💡 .. joins, + adds. If you mean text, use ..; if you see an arithmetic error on a string, that's the cause.
    • 💡 Convert on purpose. Reach for tonumber the moment a value comes from user input, a file, or the network — it's text until you say otherwise.
    • 💡 Multi-line strings use double square brackets: [[ ... ]]. They keep newlines and whitespace exactly, great for templates.

    Common Errors (and the fix)

    • Forgetting local makes a global: health = 50 quietly pollutes the global namespace, and a typo creates a stray global. Fix: write local health = 50.
    • "attempt to perform arithmetic on a string value": you joined text with +, e.g. "Hi " + name. Fix: use .."Hi " .. name.
    • "attempt to compare number with string": Lua won't compare across types, e.g. 10 < "20". Fix: convert first — 10 < tonumber("20").
    • "attempt to perform arithmetic on a nil value": you used a variable that was never set (or a tonumber that returned nil) in maths, like nil + 1. Fix: check the value exists before doing arithmetic.

    📋 Quick Reference

    TaskCodeResult
    Local variablelocal x = 10scoped to block
    Check a typetype("hi")"string"
    Join text"a" .. "b""ab"
    String length#"hello"5
    Text → numbertonumber("42")42
    Number → texttostring(42)"42"
    No valuelocal x = nilnil

    Frequently Asked Questions

    Q: Why should I almost always use local instead of global?

    A local variable only exists inside the block where you declare it, so it can't accidentally clash with code elsewhere, and the Lua compiler can access it faster. Globals live in one shared table that the whole program (and every library) can see and overwrite, so a single typo can silently break distant code. The rule of thumb: write 'local' in front of every variable unless you have a specific reason not to.

    Q: What happens if I forget the word local?

    Lua does not error — it creates a GLOBAL variable instead. That is why typos are dangerous: writing 'socre = 100' instead of 'score = 100' makes a brand-new global called socre, and your real score variable is never updated. Nothing warns you. Putting 'local' on declarations makes such mistakes far easier to catch.

    Q: How many data types does Lua have?

    Eight: nil, boolean, number, string, table, function, userdata, and thread. Beginners mainly use the first five. 'function' values let you store and pass behaviour; 'userdata' wraps data from C libraries; 'thread' is for coroutines. You can always check any value's type at runtime with type(value).

    Q: Why do I join strings with .. and not +?

    In Lua the .. operator means 'concatenate' (stick text together), while + is reserved strictly for number arithmetic. Writing "Hi " + name throws an 'attempt to perform arithmetic on a string value' error. Use "Hi " .. name instead. Numbers used inside .. are converted to text automatically.

    Q: What is nil and when do I see it?

    nil means 'no value here'. An unset variable, a missing table key, and a tonumber() call on non-numeric text all give back nil. Reading a variable that was never assigned returns nil rather than crashing — but doing arithmetic on nil (like nil + 1) is an error, so check for nil before using a value you are unsure about.

    Mini-Challenge: Character Sheet

    No blanks this time — just a brief and an outline to keep you on track. Build it, run it, and check your output against the example in the comments. This is the kind of tiny program real games and tools are stitched together from.

    🎯 Mini-Challenge: build a character sheet
    -- 🎯 MINI-CHALLENGE: Character sheet
    -- 1. Create LOCAL variables: name (string), class (string),
    --    hp (number), and alive (boolean set to true).
    -- 2. Print a 3-line sheet using  ..  to join text and values:
    --       Name: <name> the <class>
    --       HP:   <hp>
    --       Alive: <alive>
    -- 3. BONUS: print the length of the name with  #name  on its own line.
    --
    -- ✅ Example output (name="Mara", class="Mage", hp=30):
    -- Name: Mara the Mage
    -- HP: 30
    -- Alive: true
    -- Name length: 4
    
    -- your code here
    Lua runs outside the browser — try this code atonecompiler.com/luaor locally with the lua command.

    🎉 Lesson Complete!

    • ✅ Variables are global by default — write local to keep them scoped and safe
    • ✅ A forgotten local (or a typo) silently creates a global
    • ✅ Lua has 8 types; you'll mostly use nil, boolean, number, string, table
    • ✅ Lua is dynamically typed — the value has a type, the variable name doesn't
    • ✅ Join text with .., measure it with #, convert with tostring / tonumber
    • nil means "no value"; arithmetic on nil errors, so check first
    • Next lesson: Functions and Tables — package behaviour and store structured data

    Sign up for free to track which lessons you've completed and get learning reminders.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service

    Install LearnCodingFast

    Learn faster with the app on your home screen.