__gc metamethod never fires?

Overview


I’m working on a Vector3 module to cover my specific needs on my current projects. At the moment, my Vector3 class contains references to pre-made directional vectors that are created when the module is required.

--Vector3.Forward references an already existing vector (0, 0, 1)
ForwardVector = Vector3.Forward --Same as bottom
ForwardVector = Vector3.new(0, 0, 1) --Same as top

Normally a developer is able to directly make changes to the X,Y,Z components of any vector, which gets rid of expensive table creation when computing operations on vectors.

My module:

Vector.Y = Y - 5

Roblox:

--This is bad when you're performing huge vector operations thousands of times a second
Vector = Vector - Vector3.new(0, 5, 0)

The Problem


I’m open sourcing the module.

I don’t want developers to accidentally make changes to the pre-made vectors so my solution was to use userdatas to manage all behavior (To force read-only tables).

I’m using userdatas because none of the data will actually be stored inside of it, allowing __index and __newindex every time (I also have a use for the __len method).

My idea was to cache vector data into a private table that’s stored inside the class module and then delete the data from the cache when the userdata is GCed. However, the __gc metamethod is never fired?

__gc test script:

local Cache = {}

local Class = {}

function Class.new()
	local Userdata = newproxy(true)
	local UserdataMetatable = getmetatable(Userdata)
	
	local Key = "ABC"
	Cache[Key] = {"Random", "Data"}
	
	UserdataMetatable.__gc = function()
		Cache[Key] = nil
	end
	
	return Userdata
end

local Test = Class.new()
Test = nil --__gc is never fired after this?

Questions


  1. Is there a better way to create read-only objects in lua?
  2. Am I misunderstaning the way garbage collection works?
  3. I’ve seen posts saying __gc was removed but the wiki documentation doesn’t say anything regarding that. Could someone please clarify this?

Edit: Also wanted to add that I’m trying to create some of the functionality I found useful in unreal engine. Just so you can get an idea of what I’m trying to achieve.

__gc is removed from roblox. They just never changed or removed the documentation.

That’s a bummer. I had a few use cases in mind :pensive:.

Is there any other way I could create read-only objects?

You can do it by creating a proxy table which uses __index to retrieve the values from original table, while using __newindex to prevent setting new values to proxy table.

local original = {key=5}
local proxy = setmetatable({}, {
  __index = function(self, k)
    return rawget(original, k)
  end,
  __newindex = function(self, k, v) end
})

print(proxy.key) --> 5
proxy.key = 3
print(proxy.key) --> 5

That wouldn’t work for my case because original needs to be deleted as soon as the proxy is GCed.

__gc was removed as it could run code outside of the sandboxed enviroment :c

Edit
I am not sure if you can use a weak table in this case to gc the data?

1 Like

I switched to using weak tables about 10 minutes after my last post. I’ll still mark your post as the solution though.

1 Like

np glad that a solution was found.