いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-503 (noCopy)(構造体, 値コピー防止のためのテクニック)

関連記事

GitHub - devlights/blog-summary: ブログ「いろいろ備忘録日記」のまとめ

概要

以下、自分用のメモです。

すごく利用することは無いのですが、たまーに使いたいときがありますので、忘れないようここにメモメモ。。。

sync.Mutexsync.WaitGroup などのように、構造体を値コピーされると困る構造体が必要になることがあります。

そんな場合、コンパイル自体を失敗させることは出来ないのですが、go vet で警告を出力させるようには出来ます。

go vet は、sync.Locker インターフェースを実装している構造体が値コピーされている箇所を検出すると警告を出してくれますので、それを利用します。

サンプル

main.go

package main

import (
        "fmt"
        "sync"
        "sync/atomic"
)

// 構造体のコピーを防止するための構造体。sync.Lockerを空実装する。
type noCopy struct{}

func (*noCopy) Lock()   {}
func (*noCopy) Unlock() {}

// impl check
var _ sync.Locker = (*noCopy)(nil)

// 値コピーを防止したい構造体。noCopyを内部で持つように定義する。
type StA struct {
        _ noCopy
        v atomic.Int32
}

func main() {
        var (
                a = StA{}
        )

        a.v.Add(int32(1))
        fn(a) // この部分で go vet が警告を出力してくれる

        fmt.Printf("%v\n", a.v.Load())
}

func fn(a StA) { // ここも sync.Locker を実装しているオブジェクトを値コピーしているので go vet で警告が出る
        a.v.Add(int32(99))
}

Taskfile.yml

# https://taskfile.dev

version: '3'

tasks:
  default:
    cmds:
      - goimports -w .
      - go vet .
      - go run .
    ignore_error: true

この処理は、コンパイルは通りますが、値コピーして渡しているので正しい結果 (100) が表示されませんが、go vet を通すと警告を出力してくれます。

実行

$ task
task: [default] goimports -w .
task: [default] go vet .
# app/cmd/nocopy
# [app/cmd/nocopy]
./main.go:30:5: call of fn copies lock value: app/cmd/nocopy.StA contains app/cmd/nocopy.noCopy
./main.go:35:11: fn passes lock by value: app/cmd/nocopy.StA contains app/cmd/nocopy.noCopy
task: [default] go run .
1

ちゃんと警告でていますね。

上のソースコードをポインタで処理するように調整すると、警告は出なくなり、結果も正しくなります。

func main() {
        var (
                a = &StA{}
        )

        a.v.Add(int32(1))
        fn(a)

        fmt.Printf("%v\n", a.v.Load())
}

func fn(a *StA) {
        a.v.Add(int32(99))
}
$ task
task: [default] goimports -w .
task: [default] go vet .
task: [default] go run .
100

参考情報

mattn.kaoriya.net

Goのおすすめ書籍


過去の記事については、以下のページからご参照下さい。

サンプルコードは、以下の場所で公開しています。