I came across the words, weak tables and strong tables. I have done some research; this is what I understood from the terms:
Weak tables can have weak keys. Weak tables can have weak values. Weak tables can have weak keys and weak values. The garbage collector can return the memory of weak references.
I am assuming that strong tables are the opposite.
To make a table have weak keys, you can use the metamethod __mode
.
local t t = {} setmetatable(t, {__mode = "k"})
To make a table have weak values, you type in "v".
local t t = {} setmetatable(t, {__mode = "v"})
The question is, what is the use of weak tables and strong tables. Please connect this with some type of everyday coding life. If I did miss anything about these types of tables, please put them in your answer.
Sources: Lua.org source Roblox Developer Wiki
I can't say I've ever heard of strong tables before so I can't provide much insight for that. Weak tables however I understand a bit better.
The first thing I'll say is that weak tables have limited uses. If you find yourself trying to use them when there's a another solution you're probably just making life more difficult for yourself. That being said, when using them correctly they're extremely useful.
I think the most important line from the wiki in regards to weak tables is the following: "If a reference to an object is only held inside weak tables, Lua will collect the object eventually."
What this means is that depending on our mode if the keys or values or one of the two (both) aren't referenced anywhere else in the active code their entry will be removed from the table.
Let's look at some examples:
Note: the garbage may not collect in the 1 second interval so if having difficulties either run again or change the wait time.
local t = setmetatable({}, {__mode = "k"}); local k = game.Workspace.Part; -- say this exists t[k] = "Value"; for k, v in next, t do print(k, v); end -- Part, Value wait(1); k = nil; -- no longer any reference to the key other than in the weak table -- the key and value still print though b/c garbage has not collected yet for k, v in next, t do print(k, v); end -- Part, Value wait(1); -- wait for garbage to collect for k, v in next, t do print(k, v); end -- nothing... print("Done.")
Now looking at values:
local t = setmetatable({}, {__mode = "v"}); local v = game.Workspace.Part; -- say this exists t["Key"] = v; for k, v in next, t do print(k, v); end -- Key, Part wait(1); v = nil; -- no longer any reference to the value other than in the weak table -- the key and value still print though b/c garbage has not collected yet for k, v in next, t do print(k, v); end -- Key, Part wait(1); -- wait for garbage to collect for k, v in next, t do print(k, v); end -- nothing... print("Done.")
Finally keys and values:
local t = setmetatable({}, {__mode = "kv"}); local k = game.Workspace.Part1; local v = game.Workspace.Part2; t[k] = v; for k, v in next, t do print(k, v); end -- Part1, Part2 wait(1); v = nil; -- no longer any reference to the value other than in the weak table -- the key and value still print though b/c garbage has not collected yet for k, v in next, t do print(k, v); end -- Part1, Part2 wait(1); -- wait for garbage to collect for k, v in next, t do print(k, v); end -- nothing... print("Done.") -- OR local t = setmetatable({}, {__mode = "kv"}); local k = game.Workspace.Part1; local v = game.Workspace.Part2; t[k] = v; for k, v in next, t do print(k, v); end -- Part1, Part2 wait(1); k = nil; -- no longer any reference to the key other than in the weak table -- the key and value still print though b/c garbage has not collected yet for k, v in next, t do print(k, v); end -- Part1, Part2 wait(1); -- wait for garbage to collect for k, v in next, t do print(k, v); end -- nothing... print("Done.")
I think the main thing you need to be wary other than setting variables to nil explicitly is scope. If you create entries in a table using variables that are local in scope they will become nil when you leave that scope. Here's what I mean:
local t = setmetatable({}, {__mode = "k"}); do -- new scope local k = game.Workspace.Part; t[k] = "Value"; end -- prints eb/c garbage not collected yet for k, v in next, t do print(k, v); end wait(3); -- collect garbage -- nothing prints for k, v in next, t do print(k, v); end
So when is this useful? I personally have found this most useful for OOP (object oriented programming). We can use weak-tables to store private-values that we don't want the user to mess with in the class.
Here's an example:
local class = {}; local class_mt = {__index = class}; local class_ref = setmetatable({}, {__mode = "k"}); function class.new(x, y) local self = {}; self.x = x; self.y = y; self = setmetatable(self, class_mt); class_ref[self] = {}; class_ref[self].magnitude = math.sqrt(x*x + y*y); return self; end function class:getMagnitude() return class_ref[self].magnitude; end local a = class.new(1, 2, 3); local b = class.new(-3, 7, 32/4*(-8)); print(a.x, b.y); print(a:getMagnitude()); -- both entries class_ref[a] and class_ref[b] exist for k, v in next, class_ref do print(k, v); end b = nil; -- note: b was the only reference to this object outside a weak table wait(1); -- garbage collection -- only entry class_ref[a] exists for k, v in next, class_ref do print(k, v); end
Of course these "private" values could be accessed as long as the programmer has access to the class_ref
table, but if you're writing your class in a module then it's as simple as not returning said table. I use this exact technique to store cframe components in my lua cframe class.
Hope this helped clear some stuff up.