標準愚痴出力

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

go-lazy 二値対応 ("sync".OnceValues 相当品)

Go で遅延初期化を実現するパッケージ:go-lazy を更新した。

このパッケージと同様のものは

でも記載したとおり、Go 1.21 で実装された("sync".OnceValue)。ゆえに go-lazy は基本用済みだが、Go 1.21 以降は Windows 7,8.1,2008Server などのサポートが切られているため、まだ Go 1.18〜1.20 では需要がある。

また、"sync".OnceValue と違い、プログラム起動時に関数コールが発生しないため、少し軽いというメリットもあった。

そんな go-lazy もサポートしていない機能があった。それは "sync".OnceValues にように複数の値を返すような場合である。どういう時に利用するか、当初分からなかったが、最近、遭遇した。それはエラーが発生するような初期化関数を呼ぶ場合だった。

var ConInHandle = sync.OnceValues(func() (uintptr, error) {
    conin := []uint16{'C', 'O', 'N', 'I', 'N', '$', 0}
    h, err := windows.CreateFile(
            &conin[0],
            windows.GENERIC_READ|windows.GENERIC_WRITE,
            uint32(windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE),
            nil,
            windows.OPEN_EXISTING,
            windows.FILE_ATTRIBUTE_NORMAL,
            0)
    if err != nil {
            return 0, fmt.Errorf("windows.CreateFile: %w", err)
    }
    return uintptr(h), err
})

ConInHandle は標準入力がリダイレクトされていても端末入力するためのハンドルだが、利用するためには "CONIN$" をオープンしなければいけない。だが失敗する場合もある。だが、パニックはさせたくなかった。ゆえに呼び出し元に毎回エラー値をハンドルともども返すようにしている。

ゆえに利用箇所では次のように毎回エラーチェックをしなければいけないが、まぁ仕方がない(Goプログラマーなら慣れっこのはず)

    in, err := ConInHandle()
    if err != nil {
        return nil, err
    }

go-lazy版では、次のようになる

var Handle = lazy.Two[uintptr, error]{
    New: func() (uintptr, error) {
        conin := []uint16{'C', 'O', 'N', 'I', 'N', '$', 0}
        h, err := windows.CreateFile(
            &conin[0],
            windows.GENERIC_READ|windows.GENERIC_WRITE,
            uint32(windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE),
            nil,
            windows.OPEN_EXISTING,
            windows.FILE_ATTRIBUTE_NORMAL,
            0)
        if err != nil {
            return 0, fmt.Errorf("windows.CreateFile: %w", err)
        }
        return uintptr(h), err
    },
}
    in, err := Handle.Values()
    if err != nil {
        return nil, err
    }