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
- Tables
- Strings
- Functions
- Properties
- Variables
- Conditional Expressions & Equality
- Blocks
- Whitespace
- Commas
- Semicolons
- Type Casting & Coercion
- Naming Conventions
- Modules
- File Structure
- Testing
- Contributors
- License
-
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
-
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 }
-
Use single quotes
''
for strings.-- bad local name = "Bob Parr" -- good local name = 'Bob Parr' -- bad local full_name = "Bob " .. self.last_name -- good local full_name = 'Bob ' .. self.last_name
-
Strings longer than 80 characters should be written across multiple lines using concatenation. This allows you to indent nicely.
-- bad local error_message = '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 error_message = '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 error_message = [[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 error_message = '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.'
-
Prefer lots of small functions to large, complex functions. Smalls Functions Are Good For The Universe.
-
Prefer function syntax over variable syntax at the top level.
-- AVOID local nope = function(name, options) -- ...stuff... end -- BETTER 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
-
Use dot notation when accessing known properties.
local luke = { jedi = true, age = 28 } -- bad local is_jedi = luke['jedi'] -- good local is_jedi = 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 get_prop(prop) return luke[prop] end local is_jedi = get_prop('jedi')
-
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 = get_name() if name == 'test' then return false end return name end -- good local function good() local name = get_name() test() print('doing stuff..') //..other stuff.. if name == 'test' then return false end return name end
-
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
-
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
-
Use two spaces for indentation.
-- 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.
-
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, }
-
Nope. Separate statements onto multiple lines.
-- bad local whatever = 'sure'; a = 1; b = 2 -- good local whatever = 'sure' a = 1 b = 2
-
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 total_score = review_score .. '' -- good local total_score = tostring(review_score)
-
Use
tonumber
for Numbers.local input_value = '4' -- bad local val = input_value * 1 -- good local val = tonumber(input_value)
-
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
-
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.)
-
Files should be named in all lowercase.
-
Lua files should be in a top-level
src
folder. The main library file should be calledmodulename.lua
. -
Rockspecs, license, readme, etc should be in the top level.
-
Tests should be in a top-level spec folder.
-
Executables should be in a top-level bin folder.
-
Example:
./my_module bin/ script.sh spec/ my_module_spec.lua some_file.lua src/ my_module.lua some_file.lua README.md LICENSE.md
-
Use busted and write lots of tests in a /spec folder. Separate tests by module.
-
Use descriptive
describe
andit
blocks so it's obvious to see what precisely is failing. -
Test interfaces. Don't test private methods. If you need to test something that is private, it probably shouldn't be private in the first place.
-
Example:
./my_module bin/ script.sh spec/ my_module_spec.lua util/ formatters_spec.lua src/ my_module.lua util/ formatters.lua README.md LICENSE.md
- Released under CC0 (Public Domain). Information can be found at http://creativecommons.org/publicdomain/zero/1.0/.