Nanfeng

Notes on software development, code, and curious ideas

Shallow and Deep Copying in Lua

Assigning a table does not copy it:

1
2
3
4
local original = { score = 10 }
local alias = original
alias.score = 20
print(original.score) -- 20

Both variables refer to the same object.

Shallow copy

1
2
3
4
5
6
7
local function shallowCopy(source)
local result = {}
for key, value in pairs(source) do
result[key] = value
end
return setmetatable(result, getmetatable(source))
end

Top-level fields are copied, but nested tables remain shared. Whether the metatable should be copied, shared, or omitted is a design choice—not a universal rule.

Deep copy with cycle preservation

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
local function deepCopy(value, copies)
if type(value) ~= "table" then return value end

copies = copies or {}
if copies[value] then return copies[value] end

local result = {}
copies[value] = result

for key, item in next, value do
result[deepCopy(key, copies)] = deepCopy(item, copies)
end

local mt = getmetatable(value)
if mt then setmetatable(result, mt) end
return result
end

The memo table prevents infinite recursion and preserves shared references. This implementation deeply copies table keys but shares the original metatable. Depending on the data model, either decision may be wrong.

Functions, userdata, threads, and native resources cannot generally be cloned. Weak tables, protected metatables, object identity, and external handles also require explicit policies. In many applications, a type-specific clone method is safer than a generic deep-copy utility because it documents exactly which state is owned and which is shared.

+