Issue Type: Other
Impact: Moderate
Frequency: Constantly
Date First Experienced: 2021-03-06 00:03:00 (-05:00)
Date Last Experienced:
Reproduction Steps:
The Instance:GetAttribute(attr)
function is defined in documentation to return nil if an attribute with the given name is not set on the object. It doesn’t actually do this - the function returns nothing (void), which is inherently different. This can cause unexpected problems if you, for example, use the result of GetAttribute in calling a function which expects a value.
To demonstrate the issue, see the following expression which can be evaluated in the command bar (either by using print or prepending with an equals sign):
type(game:GetAttribute("ThisDoesNotExist"))
The type function expects exactly one value. The type function raises an error: “missing argument #1” because GetAttribute did not return nil. It’s the same thing as just calling type(), which doesn’t make any sense. If GetAttribute properly returned nil, we would’ve seen the string “nil” as a result of the type function, just as if we used type(nil). This is inconsistent with other functions which return nil properly, such as FindFirstChild:
type(game:FindFirstChild("ThisDoesNotExist"))
This expression returns the string “nil” as expected because no such object exists with this name, and FindFirstChild is defined to return nil under these conditions. Unfortunately, GetAttribute does not follow this paradigm.
Consider also the following Lua functions:
function noOp() end
function noOpNil() return nil end
The bug is that GetAttribute behaves like noOp instead of noOpNil when the attribute is not assigned.
It should be noted that this issue is not observable if you were to assign the result to a variable:
local v = game:GetAttribute("ThisDoesNotExist")
print(type(v)) --> nil
Furthermore, if used with the == comparison operator, this issue is also not observable.
game:GetAttribute("ThisDoesNotExist") == nil
Expected Behavior:
Continuing from the example in the repro steps, I expect the actual nil value. Instead, I get what I’m assuming is C++ void instead of Lua nil. It should behave like this function does:
function noOpNil() return nil end
Actual Behavior:
Continuing from the example in the repro steps, I actually get no value at all, causing the described issue. Again, I’m assuming this has to do with C++ void instead of Lua’s nil. It actually behave as if you called it this:
function noOp() end
Workaround:
You can use “or nil” after the function call to achieve the desired effect:
game:GetAttribute("ThisDoesNotExist") or nil
print(type(game:GetAttribute("ThisDoesNotExist") or nil)) --> nil
This doesn’t break under either the current or intended behavior, as non-nil values won’t get replaced by the nil, except void (which subsequently is interpret as nil, and “nil or nil” is just, well, nil).