Can you do a absolute variable reference in LUA?

I’m not sure if I’m phrasing this correctly, but is there a way to set an absolute reference in lua?
lets say I have:

originalHRP = game.Players[Player1.Value].Character:FindFirstChild("HumanoidRootPart")

is there a way to make sure “originalHRP” ONLY refers to the HRP it was initially set to? I don’t want it to do :FindFirstChild() every time. If the player dies and a new character is created, I want originalHRP to return nil (without having to implement something using like onDied)

I think it would be possible if i did something like

AbsoluteHRP = game.Players[Player1.Value].Character:FindFirstChild("HumanoidRootPart")
originalHRP = AbsoluteHRP

but I don’t know if theres a better way of going about this

That’s called typechecking. You can’t typecheck “HumanoidRootPart”, but you can make it so that variable can only contain BaseParts, in this case you can do this:

local BasePartOnly: BasePart = workspace.Part

This is not possible in Luau - variables don’t automatically clean themselves up like this. You have to manually set your variables to nil when you’re done with them, if you desire that behaviour.

(except for weak tables, but that’s advanced and doesn’t have useful behaviour for instances anyway)

1 Like

doesn’t have useful behaviour for instances anyway

Sorry, this is a bit off topic but could you explain what the actual behavior is? I’d really like to know

If you parent the script to the StarterPlayerScripts folder then it’ll only execute when the player first joins, thus when their character dies the reference to the HumanoidRootPart instance will be invalid and instead point to nil.

To prevent errors you can wrap any code which manipulates/references the HumanoidRootPart inside a conditional statement.

if HRP then
	--do code
else
	--do other code
end

Alright, but keep in mind this is pretty deep into Roblox technicals. I don’t expect you to understand this necessarily.

In Luau, there are two main kinds of reference - strong references, which stop an object from being garbage collected, and weak references, which do not stop an object from being garbage collected.

Every single reference in Luau is strong, except for when you explicitly mark either the keys or values of a table as ‘weak’ (i.e. all keys/values should be weak references) using the __mode metatable. In this case, any value that gets garbage collected will become nil.

-- even if an object appears in this table's keys, it can still be garbage collected, at which point it would be set to nil:
local weakKeys = setmetatable({}, { __mode = "k"})
-- you can get similar behaviour for this table's values: 
local weakValues = setmetatable({}, { __mode = "v"})
-- or you can have both at the same time:
local weakKeysAndValues = setmetatable({}, { __mode = "kv"})

Now for an interesting observation about instances - you can parent them into the data model, lose your reference, and get a reference back via the data model:

local reference = Instance.new("Folder")
reference.Parent = workspace

-- lose the reference...
reference = nil

-- and get one back
reference = workspace.Folder

Notice how this allows you to keep access to instances which you don’t hold any strong references to.

The way this is implemented (at least right now) is that Instance objects in Lua act like ‘pointers’ to the Instance data stored in the data model. You could, in theory, have two different Instance objects that point to the same data.

If you struggle to understand that, imagine how you can have two different tables that point to the same data stored in an array, and you’re kind of halfway there:

local data = {"Hello", "world", "from", "Luau"}

-- these two values are structurally equal, but referentially distinct
local object1 = { index = 2 }
local object2 = { index = 2 }

print(object1 == object2) -- false
print(data[object1] == data[object2]) -- true

This is exactly what happens with Instance objects - internally, they just point to some data elsewhere, and there’s nothing special or unique about any one of them. But you’ve probably noticed that this still works:

print(workspace.Folder == workspace.Folder) -- true

To make sure two instance objects that point to the same instance data are always referentially equal, Roblox keeps track of all instance objects which are still being strongly referenced. When you get an instance object from the data model, it tries to re-use an existing, strongly-referenced instance object. In other words, once it creates the first instance object for some instance data, you’ll get that same object back when you go to fetch it again.

Extending the example from earlier:

local data = {"Hello", "world", "from", "Luau"}

type Object = { index: number }

local existingObjects = setmetatable({}, { __mode = "v" })
local function getObject(withIndex: number): Object
    if existingObjects[withIndex] then
        return existingObjects[withIndex]
    else
        local obj: Object = {index = withIndex}
        existingObjects[withIndex] = obj
        return obj
    end
end

-- these two values are now both structurally equal and referentially equal
local object1 = getObject(2)
local object2 = getObject(2)

print(object1 == object2) -- true
print(data[object1] == data[object2]) -- true

An important detail to note here is that the internal cache uses weak references - that is, if you lose all the references to an instance, Roblox may garbage collect the instance object, and so you may not get the same object back.

This leads to a rather interesting, if unintuitive, behaviour for instances in weak tables - they will stick around as long as you hold a reference to the instance in code, but they ignore whether the instance is present in the data model. Depending on your use case, this might be fine, but ultimately it’s not widely useful.

9 Likes