標準愚痴出力

個人的なIT作業ログです。もしかしたら一般的に参考になることが書いているかもしれません(弱気

今更だけど、Go では (具体的なレシーバー.メソッド名) を「関数ポインタ」扱い出来た件

たぶん、有識者では常識なんだろうけれども、最近まで認識できていなかった件についてのポエム

type T struct {
    // :
}

func (t *T) Method(){
    // :
}

var t T

と定義されている場合、

var f func() = t.Method

という代入が可能。これの何が嬉しいのかというと、コールバック関数を渡さないといけない時に、具体的な値をあわせて引き渡すことが可能だという点。

昔の自分はそういうのを知らなかったので、呼び出し元で context.WithValue で引き渡すべき値を context.Context に添付し、コールバック関数側は context.Value で引き出すという方法を使っていた。それが悪いというわけではないが、本来静的チェックできるものを動的にチェックとなるので、まれに漏れがあって実行時エラーになったりする。

実際、nyagos にそういうコードが残ってたので、ちょっとそういうの、ちょっとずつ直してる。

( nyagos は Lua 依存のところと、Lua 非依存のところを明確に分けているので、コールバック関数を設定するところで、Lua 引数を想定させていない箇所も多いのだ )

旧コード

type luaKeyT struct{}

var luaKey luaKeyT

// コールバック関数側
func onCommandNotFound(ctx context.Context, sh *shell.Cmd, err error) error {
     L, ok := ctx.Value(luaKey).(Lua)
     if !ok {
          return errors.New("could get lua instance(on_command_not_found)")
     }
     // : 中略
}

// コールバック関数を設定する側
shell.OnCommandNotFound = onCommandNotFound

新コード

type _LuaCallBack struct {
    Lua
}

// コールバック関数側
func (this *_LuaCallBack) onCommandNotFound(ctx context.Context, sh *shell.Cmd, err error)
error {
    L := this.Lua
     // : 中略
}

// コールバック関数を設定する側
shell.OnCommandNotFound = (&_LuaCallBack{Lua: L}).onCommandNotFound

これ、設定漏れがあると could get lua instance(on_command_not_found) という実行時エラーになるんだけど、実際、発生するケースが見付かったりした。

context.WithValue は便利そうに見えて使用事例が意外とないのは、こんな風な値の引き渡し方法が既にあるから、使うケースがレアであるべき…ということがあるのかもしれないなぁ。