前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Sweet Snippet 之 Lua readonly table

Sweet Snippet 之 Lua readonly table

作者头像
用户2615200
发布2021-01-21 19:46:07
1K1
发布2021-01-21 19:46:07
举报

Lua table 用作静态配置是常见的使用情境,而用作静态配置的 Lua table 往往都有保持只读的需求,本文简单介绍了一些让 Lua table 变更为只读的知识 (代码基于 Lua 5.4)

基础

基础变更 Lua table 为只读的方法,在 《Programming in Lua》 中就已经给出了(这里),基本思路即是通过 __index 和 __newindex 两个元方法来做 table 的读写限制,代码大体如下:

代码语言:javascript
复制
function readonly(t)
    local proxy = {}
    local mt = 
    {
      __index = t,
      __newindex = function()
          error("attempt to update a readonly table", 2)
      end
    }
    setmetatable(proxy, mt)
    return proxy
end

简单测试一下:

代码语言:javascript
复制
local r_t = readonly({ 1, 2, 3 })
print(r_t[1])
-- error here : attempt to update a readonly table
r_t[1] = 2
完善

上述的示例代码中,虽然我们已经让 table 变为了只读,但是获取 table 长度(#)或者使用 pairs 遍历 table 时都不能得到正确结果(使用 ipairs 可以得到正确结果):

代码语言:javascript
复制
local r_t = readonly({ 1, 2, 3 })

-- correct
for k, v in ipairs(r_t) do
    print(tostring(k) .. " = " .. tostring(v))
end

-- error
print(#r_t)

-- error
for k, v in pairs(r_t) do
    print(tostring(k) .. " = " .. tostring(v))
end

完善的方法也很简单,添加相应的 __len 和 __pairs 元方法即可:

代码语言:javascript
复制
function readonly(t)
    local proxy = {}
    local mt = 
    {
      __index = t,
      __newindex = function()
          error("attempt to update a readonly table", 2)
      end,
      __len = function()
          return #t
      end,
      __pairs = function()
          return next, t, nil
      end
    }
    setmetatable(proxy, mt)
    return proxy
end
进阶

上面的示例代码中仍然存在一个比较大的问题:如果 table 中存在另外的 table 元素,经过上述 readonly 函数处理之后,这些 table 子元素仍然不是只读的:

代码语言:javascript
复制
local r_t = readonly({ 1, 2, 3, {} })
r_t[1] = 1 -- error
r_t[4] = {} -- error
r_t[4][1] = 1 -- correct ...

为了解决这个问题,我们需要递归的对 table 做 readonly 操作,相关代码如下:

代码语言:javascript
复制
local proxies = {}

function readonly(t)
    if type(t) == "table" then
        local proxy = proxies[t]
        
        if not proxy then
            proxy = {}
            local mt = 
            {
                __index = function(_, k)
                    return readonly(t[k])
                end,
                __newindex = function()
                    error("attempt to update a readonly table", 2)
                end,
                __len = function()
                    return #t
                end,
                __pairs = function()
                    local function readonly_next(t, i)
                        local n_i, n_v = next(t, i)
                        return n_i, readonly(n_v)
                    end
                    return readonly_next, t, nil
                end
            }
            setmetatable(proxy, mt)
            proxies[t] = proxy
        end
        
        return proxy
    else
        return t
    end
end

示例代码并没有对 table 进行全量的只读变更(我们自然也可以这么做),而是在访问 table 元素时以增量方式进行的,这有益于分摊程序消耗.

问题

经过了上面几步, readonly 函数已经几近完善,但仍然存在问题,如果我们使用 rawset(类似的还有 rawget) 绕过元方法来设置 table,那么 table 仍然会被更新(而不能做到只读):

代码语言:javascript
复制
local r_t = readonly({ 1, 2, 3, {} })
rawset(r_t, 1, 2) -- correct ...

如果需要解决这个问题,目前就需要在宿主语言侧(譬如 C)来实现只读的 table 类型,并导出给 Lua 来使用.

参考资料
  • Programming in Lua
  • Read Only Tables
  • Generalized Pairs And Ipairs
  • How can I implement a read-only table in lua?
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-01-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 基础
  • 完善
  • 进阶
  • 问题
  • 参考资料
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档