Lua Style Guide
This style guide contains a list of guidelines that we try to follow for our projects. It does not attempt to make arguments for the styles; its goal is to provide consistency across projects.
Feel free to fork this style guide and change to your own liking, and file issues / pull requests if you have questions, comments, or if you find any mistakes or typos.
Types
Primitives: When you access a primitive type you work directly on its value
string
number
boolean
nil
local foo = 1 local bar = foo bar = 9 print(foo, bar) -- => 1 9
Complex: When you access a complex type you work on a reference to its value
table
function
userdata
local foo = { 1, 2 } local bar = foo bar[0] = 9 foo[1] = 3 print(foo[0], bar[0]) -- => 9 9 print(foo[1], bar[1]) -- => 3 3 print(foo[2], bar[2]) -- => 2 2
Tables
Use the constructor syntax for table property creation where possible.
-- bad local player = {} player.name = 'Jack' player.class = 'Rogue' -- good local player = { name = 'Jack', class = 'Rogue' }
Define functions externally to table definition.
-- bad local player = { attack = function() -- ...stuff... end } -- good local function attack() end local player = { attack = attack }
Consider
nil
properties when selecting lengths. A good idea is to store ann
property on lists that contain the length (as noted in Storing Nils in Tables)-- nils don't count local list = {} list[0] = nil list[1] = 'item' print(#list) -- 0 print(select('#', list)) -- 1
When tables have functions, use
self
when referring to itself.-- bad local me = { fullname = function(this) return this.first_name + ' ' + this.last_name end } -- good local me = { fullname = function(self) return self.first_name + ' ' + self.last_name end }
Strings
Use single quotes
''
for strings.-- bad local name = "Bob Parr" -- good local name = 'Bob Parr' -- bad local fullName = "Bob " .. self.lastName -- good local fullName = 'Bob ' .. self.lastName
Strings longer than 80 characters should be written across multiple lines using concatenation. This allows you to indent nicely.
-- bad local errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.' -- bad local errorMessage = 'This is a super long error that \ was thrown because of Batman. \ When you stop to think about \ how Batman had anything to do \ with this, you would get nowhere \ fast.' -- bad local errorMessage = [[This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.]] -- good local errorMessage = 'This is a super long error that ' .. 'was thrown because of Batman. ' .. 'When you stop to think about ' .. 'how Batman had anything to do ' .. 'with this, you would get nowhere ' .. 'fast.'
Functions
Prefer lots of small functions to large, complex functions. Smalls Functions Are Good For The Universe.
Prefer function syntax over variable syntax. This helps differentiate between named and anonymous functions.
-- bad local nope = function(name, options) -- ...stuff... end -- good local function yup(name, options) -- ...stuff... end
Never name a parameter
arg
, this will take precendence over thearg
object that is given to every function scope in older versions of Lua.-- bad local function nope(name, options, arg) -- ...stuff... end -- good local function yup(name, options, ...) -- ...stuff... end
Perform validation early and return as early as possible.
-- bad local is_good_name = function(name, options, arg) local is_good = #name > 3 is_good = is_good and #name < 30 -- ...stuff... return is_bad end -- good local is_good_name = function(name, options, args) if #name < 3 or #name > 30 then return false end -- ...stuff... return true end
Properties
Use dot notation when accessing known properties.
local luke = { jedi = true, age = 28 } -- bad local isJedi = luke['jedi'] -- good local isJedi = luke.jedi
Use subscript notation
[]
when accessing properties with a variable or if using a table as a list.local luke = { jedi = true, age = 28 } local function getProp(prop) return luke[prop] end local isJedi = getProp('jedi')
Variables
Always use
local
to declare variables. Not doing so will result in global variables to avoid polluting the global namespace.-- bad superPower = SuperPower() -- good local superPower = SuperPower()
Assign variables at the top of their scope where possible. This makes it easier to check for existing variables.
-- bad local bad = function() test() print('doing stuff..') //..other stuff.. local name = getName() if name == 'test' then return false end return name end -- good local function good() local name = getName() test() print('doing stuff..') //..other stuff.. if name == 'test' then return false end return name end
Conditional Expressions & Equality
False and nil are falsy in conditional expressions. All else is true.
local str = '' if str then -- true end
Use shortcuts when you can, unless you need to know the difference between false and nil.
-- bad if name ~= nil then -- ...stuff... end -- good if name then -- ...stuff... end
Prefer true statements over false statements where it makes sense. Prioritize truthy conditions when writing multiple conditions.
--bad if not thing then -- ...stuff... else -- ...stuff... end --good if thing then -- ...stuff... else -- ...stuff... end
Prefer defaults to
else
statements where it makes sense. This results in less complex and safer code at the expense of variable reassignment, so situations may differ.--bad local function full_name(first, last) local name if first and last then name = first .. ' ' .. last else name = 'John Smith' end return name end --good local function full_name(first, last) local name = 'John Smith' if first and last then name = first .. ' ' .. last end return name end
Short ternaries are okay.
local function default_name(name) -- return the default 'Waldo' if name is nil return name or 'Waldo' end local function brew_coffee(machine) return machine and machine.is_loaded and 'coffee brewing' or 'fill your water' end
Blocks
Single line blocks are okay for small statements. Try to keep lines to 80 characters. Indent lines if they overflow past the limit.
-- good if test then return false end -- good if test then return false end -- bad if test < 1 and do_complicated_function(test) == false or seven == 8 and nine == 10 then do_other_complicated_function()end -- good if test < 1 and do_complicated_function(test) == false or seven == 8 and nine == 10 then do_other_complicated_function() return false end
Whitespace
Use soft tabs set to 2 spaces. Tab characters and 4-space tabs result in public flogging.
-- bad function() ∙∙∙∙local name end -- bad function() ∙local name end -- good function() ∙∙local name end
Place 1 space before opening and closing braces. Place no spaces around parens.
-- bad local test = {one=1} -- good local test = { one = 1 } -- bad dog.set('attr',{ age = '1 year', breed = 'Bernese Mountain Dog' }) -- good dog.set('attr', { age = '1 year', breed = 'Bernese Mountain Dog' })
Place an empty newline at the end of the file.
-- bad (function(global) -- ...stuff... end)(self)
-- good (function(global) -- ...stuff... end)(self)
Surround operators with spaces.
-- bad local thing=1 thing = thing-1 thing = thing*1 thing = 'string'..'s' -- good local thing = 1 thing = thing - 1 thing = thing * 1 thing = 'string' .. 's'
Use one space after commas.
--bad local thing = {1,2,3} thing = {1 , 2 , 3} thing = {1 ,2 ,3} --good local thing = {1, 2, 3}
Add a line break after multiline blocks.
--bad if thing then -- ...stuff... end function derp() -- ...stuff... end local wat = 7 --good if thing then -- ...stuff... end function derp() -- ...stuff... end local wat = 7
Delete unnecessary whitespace at the end of lines.
Commas
Leading commas aren’t okay. An ending comma on the last item is okay but discouraged.
-- bad local thing = { once = 1 , upon = 2 , aTime = 3 } -- good local thing = { once = 1, upon = 2, aTime = 3 } -- okay local thing = { once = 1, upon = 2, aTime = 3, }
Semicolons
Nope. Separate statements onto multiple lines.
-- bad local whatever = 'sure'; a = 1; b = 2 -- good local whatever = 'sure' a = 1 b = 2
Type Casting & Coercion
Perform type coercion at the beginning of the statement. Use the built-in functions. (
tostring
,tonumber
, etc.)Use
tostring
for strings if you need to cast without string concatenation.-- bad local totalScore = reviewScore .. '' -- good local totalScore = tostring(reviewScore)
Use
tonumber
for Numbers.local inputValue = '4' -- bad local val = inputValue * 1 -- good local val = tonumber(inputValue)
Naming Conventions
Avoid single letter names. Be descriptive with your naming. You can get away with single-letter names when they are variables in loops.
-- bad local function q() -- ...stuff... end -- good local function query() -- ..stuff.. end
Use underscores for ignored variables in loops.
--good for _, name in pairs(names) do -- ...stuff... end
Use snake_case when naming objects, functions, and instances. Tend towards verbosity if unsure about naming.
-- bad local OBJEcttsssss = {} local thisIsMyObject = {} local this-is-my-object = {} local c = function() -- ...stuff... end -- good local this_is_my_object = {} local function do_that_thing() -- ...stuff... end
Use PascalCase for factories.
-- bad local player = require('player') -- good local Player = require('player') local me = Player({ name = 'Jack' })
Use
is
orhas
for boolean-returning functions that are part of tables.--bad local function evil(alignment) return alignment < 100 end --good local function is_evil(alignment) return alignment < 100 end
Modules
The module should return a table or function.
The module should not use the global namespace for anything ever. The module should be a closure.
The file should be named like the module.
-- thing.lua local thing = { } local meta = { __call = function(self, key, vars) print key end } return setmetatable(thing, meta)
Note that modules are loaded as singletons and therefore should usually be factories (a function returning a new instance of a table) unless static (like utility libraries.)