OpenRestyの真の値と偽の値とピット


Luaの真の値と偽の値を再確認します:nilとfalseが偽であることを除いて、その他の値はすべて真です.「その他の値」という概念には、0、空の文字列、空のテーブルなどが含まれます.Luaでは、通常、論理オペレータとしてandおよびorが使用される.例えば、true and falsefalseを返し、false or truetrueを返します.
OK、復習はこれで終わり、このいくつかのルールから派生した様々な穴を見てみましょう.
最初のピット
Luaコードでは、パラメータにデフォルト値を設定する慣用として、xx = xx or valueの文が一般的に見られます.xxパラメータに値が指定されていない場合、その値はnilであり、文はvalueに値を与えます.
ここは今日私たちが出会った最初の穴です.前に言ったように、
nilとfalseを除いて
nil以外にfalseもありますよ!
コードを書くときは、前にデフォルト値を設定した文をついでにコピーします.あるいは業務の変動で、元の入参がブールタイプになり、一気にこの穴に落ちたのかもしれません.だからこの慣用法は、書くのに便利だが、注意して、お菓子をたくさん残さなければならない.
2番目のピット
前の最初の穴を見て、一部の仲間は穴を飛び越える方法を考えているかもしれません.xx = (xx == nil) and xx or valueに変更します.実際に走ってみると、この文もxxがfalseのcaseには及ばないことがわかります.これが2番目の穴です.
Luaには三元オペレータがありません!Luaには三元オペレータがありません!Luaには三元オペレータがありません!
大事なことを3回言う!Luaコードでは様々な3元オペレータのシミュレーションを見たことがあるかもしれませんが、それらはシミュレーションです.シミュレーションである以上、偽物にすぎないが、偽物で本物を乱す高模倣品があるにすぎない.a and b or cモードは、これらの高模倣品の一員です.このモードは、a and bからxを得、x or cを実行して最終結果yを得る2つの式を含む.ほとんどの場合、3元オペレータのように表現されます.しかし、残念ながらそうではありません.
bの値が偽である場合、a and bの実行結果は一定である.xが一定である場合、x or cの実行結果はcに一定である.したがって、bの値が偽である限り、最終結果はcに保たれる.
上記の例では、xxは伝達された変数なので、caseを走るだけで穴であることがわかります.しかし、bの位置が関数の戻り値であれば?例えばexpr and func1() or func2()の形式では、func 1がたまに偽値を返すだけで、1つのタイミング爆弾が埋め込まれている.やはり最初の穴のような結論で、慣用法は使えますが、お菓子をたくさん残します.
本明細書がこれで終了すると、その名前はLua であるべきである.しかし、実際のタイトルはOpenResty なので、OpenResty専用のピットについて説明します.
3番目のピット
Lua内のnilはプレースホルダとして機能しないため,redisキー対応値が空のようなデータの空きを表すためにOpenRestyはngx.nullという定数を導入した.ngx.nullはNULLのuserdataです.
$ resty -e 'print(tostring(ngx.null))'
userdata: NULL

ngx.nullはnull字を持っていますが、nilに等しいわけではありません.先頭のルール(他の値は真)に基づいてngx.nullのブール値は真です.ブール値は本当で、空の定数を表しています.正直に言うと、私はまだ他のプログラミング環境で見たことがありません.これはまた潜在的な穴です.考えているうちに、うっかりして偽の値として考えてしまうからです.例えば、redisから特定のキーを取得し、存在しない場合は関数Aを呼び出す.ngx.nullの特殊性が一時半で思い出せない場合は、戻り値が真(またはnil)かどうかを直接判断して穴に落ちてしまう可能性があります.特に、下位論理がngxをnullが包装されていて、上層部が呼び出した人はnilとvalueのほかにngxがあるとは思わなかったかもしれません.nullの存在!
local res, err = redis.get('key1')
if not res then
    ...
end

--          ,      key1    …… 500 Internal Server Error!
-- res = res + 1
--     
if res ~= ngx.null then
    res = res + 1

このような場合、下位層で外部データサービスと付き合うコードに職を止め、ngxを適切に処理することを確保する必要がある.null .具体的にどのように処理するかについては、ngx.nullをnilに変換するか、ビジネス関連のデフォルト値に変更するかは、具体的なビジネスロジックによって決まります.
4番目の穴
これまでngxを見てきましたnullという特立独行の空の値です.OpenRestyにはもう一つの空の値があります.LuaJIT FFIのcdata:NULLに由来します.ngxのようにnullはuserdataカテゴリ内のNULLであり、cdata:NULLはcdataカテゴリ内のNULLである.FFIインタフェースを介してC関数を呼び出すと、この関数はNULLポインタを返し、Luaコードから見ればcdata:NULL値を受信します.当然のことながら、このとき返される値はnilであるべきだと思うかもしれませんが、Luaの中のnilが対応しているのは、Cの中のNULLではないでしょうか.しかし、天が気に入らないので、この時帰ってきたのはcdata:NULLで、変な子です.
どうして変な子だと言ったの?nilと等しいのでngx.nullはnilに等しくありません.しかし、nilと等しいということは、nilを置き換えることができるという意味ではありません.cdata:NULLは、最初に述べたルールに従います.ブール値が真であることを意味します.もう一つのブール値は本当で、空の定数を表します!そして今回はもっと変で、この空定数はnilと等しい!
次の例のコードを見てみましょう.
#!/usr/bin/env luajit
local ffi = require "ffi"

local cdata_null = ffi.new("void*", nil)
print(tostring(cdata_null))
if cdata_null == nil then
    print('cdata:NULL is equal to nil')
end
if cdata_null then
    print('...but it is not nil!')
end

どのように処理しますか?多くの場合、FFI呼び出しが返すのがcdata:NULLかどうかを判断すれば十分である.cdata:NULLnilが等しいことを利用することができます.
#!/usr/bin/env luajit
local ffi = require "ffi"

local cdata_null = ffi.new("void*", nil)
if cdata_null == nil then
    print('cdata:NULL found')
end

次のピットのように、下部でCインタフェースと付き合うときにcdata:NULLを除去し、拡散させないでください.「災い」コードの他の部分です.