---
# System prepended metadata

title: Lua 陷阱
tags: [程式]

---

## 輪詢table中有nil而被中斷

有時候使用foreach來輪詢table的時候，有可能遇到後面的成員沒有被輪到的問題。例如：
``` lua
local tb = {1, 2, nil, 4, 5}
for idx, val in ipairs(tb) do
  print(val)
end
-- 結果：
-- 1
-- 2
```
是因為lua在輪詢時，遇到nil後就會中斷，無法判斷這個成員本身是不是nil。

所以需要：
1. 以別的for方式輪詢
``` lua
local tb = {1, 2, nil, 4, 5}
for idx = 1, #tb do
  print(tb[idx])
end
```
2. 以其他值代替nil

---

## 偽繼承的成員存取混淆問題

有時候使用setmetatable 以及 __index 來實現類似繼承效果時，會造成非預期的變數設置問題。

首先若以這樣的方式來實現繼承效果：

``` lua
-- 某一種繼承實現方式
function Inst:new (o)
  o = o or {}
  setmetatable(o, self)
  self.__index = self
  return o
end
```

會使被new出來的 空table，在取得任意成員時，因為找不到非nil的值，而會去__index中，也就是意義上的父table去尋找。
而設置時，則是在該空table中加入key value，並且讓之後要取得時是直接由此取得，也就不用進到__index中。

但是這樣做會有一些狀況，如：
1. 若子table覆寫了某一成員為nil，但父table依然存有該成員的值，那麼在對子table進行取用時，則會因為該nil而轉而向__index去從父table中取值。
    ``` lua
    local Inst = {
        val = 5
    }
    -- 某一種繼承實現方式
    function Inst:new (o)
      o = o or {}
      setmetatable(o, self)
      self.__index = self
      return o
    end
    
    local subInst = Inst:new()
    print(subInst.val) -- result: 5
    subInst.val = nil
    print(subInst.val) -- result: 5
    ```

2. 若在進行子table覆寫其中的table成員時，不是直接採用 = 來設置取代，而是將該table成員傳入其他如table.insert來使用的話，就會因為原本的子table因為是空table，所以取用來設置的反而是透過__index取得的父table中的table成員。導致可能有的資料誤用問題。

    ``` lua
    local Inst = {
        val = {"original"}
    }
    -- 某一種繼承實現方式
    function Inst:new (o)
      o = o or {}
      setmetatable(o, self)
      self.__index = self
      return o
    end
    
    -- 建立兩個子Table    
    local subInst_A = Inst:new()
    local subInst_B = Inst:new()

    -- 對 子Table A 進行加入(但實際上被取用來設置的是Inst.val)
    subInst_A.val[2] = "A"

    -- 對 子Table B 進行加入(但實際上被取用來設置的是Inst.val)
    table.insert(subInst_B.val, "B")

    -- 印出 subInst_B.val 但實際上會取到 Inst.val     
    for idx, val in ipairs(subInst_B.val) do
      print(val)
    end
    -- result: original, A, B
    ```
