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
stringnumberbooleannil
localfoo=1localbar=foobar=9print(foo, bar) -- => 1 9
Complex: When you access a complex type you work on a reference to its value
tablefunctionuserdata
localfoo= { 1, 2 } localbar=foobar[0] =9foo[1] =3print(foo[0], bar[0]) -- => 9 9print(foo[1], bar[1]) -- => 3 3print(foo[2], bar[2]) -- => 2 2
Use the constructor syntax for table property creation where possible.
-- badlocalplayer= {} player.name='Jack'player.class='Rogue'-- goodlocalplayer= { name='Jack', class='Rogue' }
Define functions externally to table definition.
-- badlocalplayer= { attack=function() -- ...stuff...end } -- goodlocalfunctionattack() endlocalplayer= { attack=attack }
Consider
nilproperties when selecting lengths. A good idea is to store annproperty on lists that contain the length (as noted in Storing Nils in Tables)-- nils don't countlocallist= {} list[0] =nillist[1] ='item'print(#list) -- 0print(select('#', list)) -- 1
When tables have functions, use
selfwhen referring to itself.-- badlocalme= { fullname=function(this) returnthis.first_name+'' +this.last_nameend } -- goodlocalme= { fullname=function(self) returnself.first_name+'' +self.last_nameend }
Use single quotes
''for strings.-- badlocalname="Bob Parr"-- goodlocalname='Bob Parr'-- badlocalfull_name="Bob " ..self.last_name-- goodlocalfull_name='Bob ' ..self.last_name
Strings longer than 80 characters should be written across multiple lines using concatenation. This allows you to indent nicely.
-- badlocalerror_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.'-- badlocalerror_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.'-- badlocalerror_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.]]-- goodlocalerror_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.
-- AVOIDlocalnope=function(name, options) -- ...stuff...end-- BETTERlocalfunctionyup(name, options) -- ...stuff...end
Never name a parameter
arg, this will take precendence over theargobject that is given to every function scope in older versions of Lua.-- badlocalfunctionnope(name, options, arg) -- ...stuff...end-- goodlocalfunctionyup(name, options, ...) -- ...stuff...end
Perform validation early and return as early as possible.
-- badlocalis_good_name=function(name, options, arg) localis_good=#name>3is_good=is_goodand#name<30-- ...stuff...returnis_badend-- goodlocalis_good_name=function(name, options, args) if#name<3or#name>30thenreturnfalseend-- ...stuff...returntrueend
Use dot notation when accessing known properties.
localluke= { jedi=true, age=28 } -- badlocalis_jedi=luke['jedi'] -- goodlocalis_jedi=luke.jedi
Use subscript notation
[]when accessing properties with a variable or if using a table as a list.localluke= { jedi=true, age=28 } localfunctionget_prop(prop) returnluke[prop] endlocalis_jedi=get_prop('jedi')
Always use
localto declare variables. Not doing so will result in global variables to avoid polluting the global namespace.-- badsuperPower=SuperPower() -- goodlocalsuperPower=SuperPower()
Assign variables at the top of their scope where possible. This makes it easier to check for existing variables.
-- badlocalbad=function() test() print('doing stuff..') //..otherstuff..localname=get_name() ifname=='test' thenreturnfalseendreturnnameend-- goodlocalfunctiongood() localname=get_name() test() print('doing stuff..') //..otherstuff..ifname=='test' thenreturnfalseendreturnnameend
False and nil are falsy in conditional expressions. All else is true.
localstr=''ifstrthen-- trueend
Use shortcuts when you can, unless you need to know the difference between false and nil.
-- badifname~=nilthen-- ...stuff...end-- goodifnamethen-- ...stuff...end
Prefer true statements over false statements where it makes sense. Prioritize truthy conditions when writing multiple conditions.
--badifnotthingthen-- ...stuff...else-- ...stuff...end--goodifthingthen-- ...stuff...else-- ...stuff...end
Prefer defaults to
elsestatements where it makes sense. This results in less complex and safer code at the expense of variable reassignment, so situations may differ.--badlocalfunctionfull_name(first, last) localnameiffirstandlastthenname=first..'' ..lastelsename='John Smith'endreturnnameend--goodlocalfunctionfull_name(first, last) localname='John Smith'iffirstandlastthenname=first..'' ..lastendreturnnameend
Short ternaries are okay.
localfunctiondefault_name(name) -- return the default 'Waldo' if name is nilreturnnameor'Waldo'endlocalfunctionbrew_coffee(machine) returnmachineandmachine.is_loadedand'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.
-- goodiftestthenreturnfalseend-- goodiftestthenreturnfalseend-- badiftest<1anddo_complicated_function(test) ==falseorseven==8andnine==10thendo_other_complicated_function()end-- goodiftest<1anddo_complicated_function(test) ==falseorseven==8andnine==10thendo_other_complicated_function() returnfalseend
Use two spaces for indentation.
-- badfunction() ∙∙∙∙localnameend-- badfunction() ∙localnameend-- goodfunction() ∙∙localnameend
Place 1 space before opening and closing braces. Place no spaces around parens.
-- badlocaltest= {one=1} -- goodlocaltest= { one=1 } -- baddog.set('attr',{ age='1 year', breed='Bernese Mountain Dog' }) -- gooddog.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.
-- badlocalthing=1thing=thing-1thing=thing*1thing='string'..'s'-- goodlocalthing=1thing=thing-1thing=thing*1thing='string' ..'s'
Use one space after commas.
--badlocalthing= {1,2,3} thing= {1 , 2 , 3} thing= {1 ,2 ,3} --goodlocalthing= {1, 2, 3}
Add a line break after multiline blocks.
--badifthingthen-- ...stuff...endfunctionderp() -- ...stuff...endlocalwat=7--goodifthingthen-- ...stuff...endfunctionderp() -- ...stuff...endlocalwat=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.
-- badlocalthing= { once=1 , upon=2 , aTime=3 } -- goodlocalthing= { once=1, upon=2, aTime=3 } -- okaylocalthing= { once=1, upon=2, aTime=3, }
Nope. Separate statements onto multiple lines.
-- badlocalwhatever='sure'; a=1; b=2-- goodlocalwhatever='sure'a=1b=2
Perform type coercion at the beginning of the statement. Use the built-in functions. (
tostring,tonumber, etc.)Use
tostringfor strings if you need to cast without string concatenation.-- badlocaltotal_score=review_score..''-- goodlocaltotal_score=tostring(review_score)
Use
tonumberfor Numbers.localinput_value='4'-- badlocalval=input_value*1-- goodlocalval=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.
-- badlocalfunctionq() -- ...stuff...end-- goodlocalfunctionquery() -- ..stuff..end
Use underscores for ignored variables in loops.
--goodfor_, nameinpairs(names) do-- ...stuff...end
Use snake_case when naming objects, functions, and instances. Tend towards verbosity if unsure about naming.
-- badlocalOBJEcttsssss= {} localthisIsMyObject= {} localthis-is-my-object= {} localc=function() -- ...stuff...end-- goodlocalthis_is_my_object= {} localfunctiondo_that_thing() -- ...stuff...end
Use PascalCase for factories.
-- badlocalplayer=require('player') -- goodlocalPlayer=require('player') localme=Player({ name='Jack' })
Use
isorhasfor boolean-returning functions that are part of tables.--badlocalfunctionevil(alignment) returnalignment<100end--goodlocalfunctionis_evil(alignment) returnalignment<100end
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.lualocalthing= { } localmeta= { __call=function(self, key, vars) printkeyend } returnsetmetatable(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
srcfolder. 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
describeanditblocks 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/.