Lua Weak Tables
A weak table is a table whose keys or values do not keep objects alive for the collector. Its entries vanish once nothing else refers to the key or the value.
Weak keys drop a registry entry
The registry below uses __mode "k". Its only key is built inside a helper, so nothing strong refers to it once the helper returns.
local function makeRegistry()
local registry = {}
setmetatable(registry, {__mode = "k"})
local player = {name = "Ada"}
registry[player] = 100
return registry
end
local function count(t)
local n = 0
for k in pairs(t) do
n = n + 1
end
return n
end
local registry = makeRegistry()
print(count(registry))
collectgarbage("collect")
print(count(registry))Weak values drop a cache entry
The cache below uses __mode "v". Its item is built inside a helper, so only the weak slot refers to it once the helper returns.
local function makeCache()
local cache = {}
setmetatable(cache, {__mode = "v"})
local item = {name = "sword"}
cache["sword"] = item
return cache
end
local cache = makeCache()
print(cache["sword"] ~= nil)
collectgarbage("collect")
print(cache["sword"] == nil)Weak keys and values together drop an entry
The table below uses __mode "kv". Both its key and its value are built inside a helper, so neither survives the helper call strongly.
local function makeBoth()
local t = {}
setmetatable(t, {__mode = "kv"})
local keyObj = {id = 1}
local valueObj = {id = 2}
t[keyObj] = valueObj
return t
end
local function count(t)
local n = 0
for k in pairs(t) do
n = n + 1
end
return n
end
local t = makeBoth()
print(count(t))
collectgarbage("collect")
print(count(t))Example
local function makeCache()
local cache = {}
setmetatable(cache, {__mode = "v"})
local item = {name = "shield"}
cache["shield"] = item
return cache
end
local function makeRegistry()
local registry = {}
setmetatable(registry, {__mode = "k"})
local player = {name = "Ada"}
registry[player] = 100
return registry
end
local function count(t)
local n = 0
for k in pairs(t) do
n = n + 1
end
return n
end
local cache = makeCache()
local registry = makeRegistry()
print(cache["shield"] ~= nil)
print(count(registry))
collectgarbage("collect")
print(cache["shield"] == nil)
print(count(registry))Key points
- Mode: the __mode field of a metatable sets a table to weak keys, weak values, or both.
- Weak keys: with __mode "k", an entry vanishes once its key object is collected.
- Weak values: with __mode "v", an entry vanishes once its value object is collected.
- Both: with __mode "kv", an entry vanishes once either its key or its value is collected.
- Cache: a weak-valued table makes a safe cache, because dropped values leave the cache by themselves.
- Registry: a weak-keyed table makes a safe registry, because dropped keys leave the registry by themselves.
In this example, makeCache builds a weak-valued cache and makeRegistry builds a weak-keyed registry, and each helper returns only its table. Before collection the cache slot holds an object and the registry holds one entry. After a full cycle the cache slot is nil and the registry count is zero, so both structures cleaned themselves up.
Note
a weak slot still holds its value until a collection cycle runs, so check the slot after collectgarbage("collect").
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Which __mode setting makes a cache drop a value once nothing else refers to it?