Roblox Scripting Assistance - Resources

Join our Discord!

The difference between nil and "void"

< Back to overview
< View all authors
< View all categories

A common misconception in Lua is that functions have an implicit return value. This is not true. Functions have an implicit return instruction, meaning a function will always return after execution even if no return is specified. This implicit return instruction will never carry any value.

This is not to be confused with nil, however. For the sake of explanation, we will call this complete absence of value void. The source code for Lua 5.1 refers to it as None but void is a much more common term in the world of programming.

The difference between the two comes from the way emptiness is represented in Lua. nil is a value that represents the absence of a value. It is possible to work with nil in Lua because it is a native datatype. For the sake of the analogy we will call nil a “tangible nothing.” It is a value that we can use that represents nothing. void on the other hand, is an “intangible nothing.” Lua does not comprehend this lack of value because it was designed to use nil as a lack of value. The only way to detect this nothingness is through C functions. Take this for example:

local function returnsVoid()
	-- nothing here! return instruction is auto generated.
end

local function returnsNil()
	return nil
end

print(select("#", returnsVoid())) --> 0
print(select("#", returnsNil())) --> 1

select is a function with some overloads, the one we’re using simply counts the number of arguments passed to it and returns it.

As we can see, there is a detectable difference. However, in most cases this void will end up being coerced into nil because it makes more sense to the user. This begs the question, “If void is coerced into nil most of the time, why does it matter?” This is the beautiful part. For the most part, it really doesn’t matter! There is a few cases where it can come back to bite you though.

Let’s reuse the functions we defined earlier for this next example.

local t = {1,2,3}

table.insert(t, returnsNil()) --> while generally useless, this code is safe and causes no issues.

table.insert(t, returnsVoid()) --> this will error during runtime because table.insert is an overloaded function! 
-- it expects 2 or 3 arguments but since void is a complete absence of value, it only sees table.insert(t)

A Roblox related example of this difference is in the function Instance:GetAttribute(). It has the capability to return void which comes with the same problems as the above example.


Author: artblart
Category: Scripting
Date: 28 September 2021

< Back to overview
< View all authors
< View all categories