Example of Weak Tables in Lua

Tables are ubiquitous data structures in Lua. Arrays, hashes, classes, objects, modules, environments—all tables. The language also allows weak tables. These are tables which are more prone to garbage collection than others; it is more likely that Lua will reclaim their memory compared to non-weak tables.

So why ever use weak tables if the language may suddenly throw away the contents? Today I’ll give an example of how that behavior can be useful.

A Summary of Tables and Weak References

To understand the behavior of weak tables we must first talk about weak references. When you perform certain assignments in Lua it will create a reference instead of performing a copy. Tables are the best and most common example of this. Consider the following code:

a = {}
a["foo"] = 10

b = a
b["foo"] = 20

print(a["foo"])

At the end it will print twenty. Why? Because the line b = a created a reference; it makes b a reference to the table a, not a copy of it. That means any modification we make to b also happens to a because they both ‘refer’ to the same table in memory. That is how our changes to the second table affect the first.

There are entire books that explain the concept of garbage collection—Richard Jones has a great list—so I am going to be brief here. Lua will reclaim the memory of an object (e.g. a table) when there are no remaining references to that object. So if there are no more variables pointing to a table then Lua is free to reclaim the memory for that table. Yet all it takes is one reference to prevent this garbage collection from happening. Unless it’s a weak reference. If the only remaining references to a table are weak references then Lua will reclaim that memory. Those references are too ‘weak’ to stop Lua’s garbage collection.

That all briefly explains how weak references work, but not why we may care to use them in Lua.

An Example of Weak Tables

A ‘weak table’ in Lua is one which has weak references for its keys or values, or both. We create weak tables by setting the __mode property of the metatable for a given table. So for example:

-- This table will have weak keys.
keys = {}
setmetatable(keys, { __mode = "k" })

-- This table will have weak values.
values = {}
setmetatable(values, { __mode = "v" })

-- This table will have weak keys and values.
both = {}
setmetatable(both, { __mode = "kv" })

Note: Lua only looks for the letters k and v inside of the __mode string. You could write anything in there, but I strongly recommend against it.

So what does it mean exactly for one of these tables to have ‘weak values’? It is common to store tables inside of other tables since that is Lua’s sole complex data structure. Let’s say we create a table with a bunch of objects to act as a cache, and we’ll make it a weak table with weak values. Why? Because if the only remaining reference to the object is the one inside of the cache, implying we’ve gotten rid of it elsewhere, then we do not want the table to keep it in memory. So Lua will reclaim that memory through garbage collection.

Here is an example from code actually in-use:

-- activeCharacters: This is a table of all of the active
-- characters in the scene.  Each time we render the contents of
-- the scene we also render all active characters.  An individual
-- Character object has all of the data to know where it should
-- appear on screen, so all the scene must do is tell each
-- character to draw itself.
--
-- Each key in this table is the name of a character, as a string.
-- The corresponding value is the Character object that represents
-- the character named by the key.
--
-- This table has weak values.  If the only reference to a
-- specific Character object is the one which remains in this
-- table then Lua will garbage-collect it.  Because we create each
-- Character outside of scenes the only time this table will have
-- the only remaining reference is when we actively start
-- destroying those external references to completely get rid of
-- that character.
self.activeCharacters = {}
setmetatable(self.activeCharacters, { __mode = "v" })

I hope that sheds some light on a situation where weak tables may be useful. To be honest I have rarely found that I need weak tables, and fellow programmers have told me the same. So I apologize if this explanation is lacking. You can find a more detailed treatment of the subject in Programming in Lua.

Advertisements

Add Your Thoughts

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s