Reading — step 1 of 5
Learn
~1 min readClosures and Tables
Lua tables hold strong references by default — keys and values are kept alive as long as the table is. Weak tables let GC collect them.
local cache = {}
setmetatable(cache, { __mode = "k" }) -- keys are weak
__mode modes:
"k"— keys are weak"v"— values are weak"kv"— both
Weak-key table: when a key is no longer referenced elsewhere, GC removes the entry.
local cache = setmetatable({}, { __mode = "k" })
local user = {name = "Ada"}
cache[user] = "some computed data"
user = nil -- the original reference is gone
collectgarbage()
-- The cache entry is also gone (key was weakly held)
Use cases:
Per-object metadata without leaking:
local metadata = setmetatable({}, { __mode = "k" })
function tag(obj, info)
metadata[obj] = info
end
When obj is collected, its metadata vanishes too — no manual cleanup.
Memoization with weak values:
local computed = setmetatable({}, { __mode = "v" })
function expensive(key)
if computed[key] then return computed[key] end
local result = doExpensiveWork(key)
computed[key] = result
return result
end
Values can be GC'd when no other references exist — natural cache eviction.
Tables themselves can be weak — singleton registries with auto-cleanup:
local connections = setmetatable({}, { __mode = "v" })
function register(conn)
connections[conn.id] = conn
end
Caveats:
- Booleans, numbers, strings are NEVER collected — weak entries with these as keys don't get cleaned up
- GC is non-deterministic — don't rely on specific cleanup timing
- Use
collectgarbage()to force a full collection (tests, benchmarks)
setmetatable(t, nil) to remove a metatable.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…