👋

Go1.19~のsync/atomicの新旧APIの使い分け

2022/10/18に公開

Go1.19のリリースでsync/atomicに型が追加され、メソッド経由でアトミック操作が行えるようになりました
標準パッケージ初のgenerics利用事例 _sync_atomic Pointer_

新しく実装する箇所に関しては基本的に新しいAPIを利用するのが推奨されます

先日仙台で開催されたGo Conference mini 2022 Autumn IN SENDAIでこのアップデートの内容について話したのですが、 従来のAPIがDeprecated(非推奨)にならない理由について説明が不足していたため、それを補足するための記事になります

https://docs.google.com/presentation/d/1fAiWGb63jq2rv5Aqrkh3bUwb75QoYhs0PEVwk-Q63HI/edit?usp=sharing
YouTubeのvideoIDが不正ですhttps://youtu.be/aVH9pBxcL1E?t=22369

従来のAPIはDeprecatedにはならない

従来のAPIでやれることは全て実現でき、利便性も高い完全上位互換な機能が生まれたため、従来のAPIはDeprecatedにしてしまった方が良いのではと思い、proposalを投稿しました

https://github.com/golang/go/issues/55302

議論の結果、提案は却下されています

APIの利用方法が変わっている

例えば構造体がもつフィールドをアトミック操作の対象にしたい場合、以下のようにフィールドの型と関数を呼び出す実装を変更する必要があります

type Foo struct {
	Counter int64
}

func (f *Foo) Count() {
	atomic.AddInt64(&f.Counter, 1)
}

---

type Foo struct {
	Counter atomic.Int64
}

func (f *Foo) Count() {
	f.Counter.Add(1)
}

これは変更としては大きく、Deprecatedにしてしまうと警告の対応に開発者の時間を多く消費してしまいます

proposalへのフィードバック#issuecomment-1254702555

似たような事例でioutilパッケージをDeprecatedにする提案がありました

今回のproposalと異なる点としては、ioutilの古いAPIと代替の新しいAPIのシグネチャがほとんど同じで、開発者が対応しやすいものとなっていました
また、ioutilの廃止によって余分なコードのインポートが減るというメリットもありました

https://github.com/golang/go/issues/42026

従来のAPIが警告を出すほど壊れていない

MD5やmath/rand.Readなど他の非推奨化が予定されている機能は、利用されることでプログラムに直接的に問題を与えてしまう可能性があります

https://github.com/golang/go/issues/20661

これらと比較すると、sync/atomicの従来のAPIは、多少利便性に欠けるものの利用されることによる問題がそれほど大きくないため非推奨にする必要はないと判断されました

proposalへのフィードバック#issuecomment-1261240285

まとめ

警告の対応により、開発者の時間を取る割にはあまりメリットがないため、従来のAPIは非推奨にはならないですが、以下の理由により新しく実装する箇所に関しては新しいAPIを利用するのが良いです

  • アトミック操作されるべき箇所がわかりやすく、安全なコードが書ける
  • Pointer型が型パラメータで型を指定でき便利(標準パッケージでgenericsが使われた初めての事例)
  • 従来のAPIでやれることは新しいAPIで全て実現できる

Discussion