makezero

Table of Contents

  1. 解決する問題
  2. 設定
  3. オプション
    1. always オプションの違い
  4. サンプル
    1. 検出例
    2. 修正例
  5. 注意点
  6. 参考リンク

makezero は make で非ゼロ長に初期化されたスライスに append しているパターンを検出するツールです。

解決する問題

make([]T, n) で長さ n のスライスを作成すると、ゼロ値の要素が n 個入った状態になります。この後に append すると、ゼロ値の要素の後ろに新しい要素が追加され、意図しないデータが含まれます。

1
2
3
4
5
6
7
8
// Before: make で長さを指定してから append → ゼロ値が残る
func CopyIDs(users []User) []int {
ids := make([]int, len(users))
for _, u := range users {
ids = append(ids, u.ID)
}
return ids // [0, 0, ..., 0, 実際のID...] ← ゼロ値が先頭に残る
}
1
2
3
4
5
6
7
8
// After: 長さ 0、容量 n で初期化
func CopyIDs(users []User) []int {
ids := make([]int, 0, len(users))
for _, u := range users {
ids = append(ids, u.ID)
}
return ids // [実際のID...] ← 正しい結果
}

設定

公式ドキュメント

1
2
3
4
5
6
7
8
9
# .golangci.yml 設定例
version: "2"

linters:
enable:
- makezero
settings:
makezero:
always: false

オプション

オプション デフォルト 説明
always bool false true にすると append の有無に関わらず、make での非ゼロ長初期化をすべて検出する

always オプションの違い

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// always: false(デフォルト)→ append がある場合のみ検出
ids := make([]int, len(users))
for _, u := range users {
ids = append(ids, u.ID) // 警告: append to slice initialized with non-zero length
}

// always: false → append がなければ検出しない(正当な使い方)
ids := make([]int, len(users))
for i, u := range users {
ids[i] = u.ID // インデックスアクセスなので問題なし
}

// always: true → append の有無に関わらず警告
ids := make([]int, len(users)) // 警告: slice initialized with non-zero length

サンプル

検出例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// makezero が警告するパターン

// 1. 基本的なパターン: 非ゼロ長スライスに append
func CollectNames(users []User) []string {
names := make([]string, len(users)) // append to slice initialized with non-zero length
for _, u := range users {
names = append(names, u.Name)
}
return names
}

// 2. 条件付き append でも検出
func FilterActive(users []User) []string {
names := make([]string, len(users))
for _, u := range users {
if u.Active {
names = append(names, u.Name)
}
}
return names
}

// 3. 変数を経由した append
func Transform(data []int) []string {
result := make([]string, len(data))
for _, d := range data {
s := strconv.Itoa(d)
result = append(result, s)
}
return result
}

修正例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 方法 1: 長さ 0、容量指定で make(append を使う場合)
func CollectNames(users []User) []string {
names := make([]string, 0, len(users))
for _, u := range users {
names = append(names, u.Name)
}
return names
}

// 方法 2: インデックスアクセスに変更(append を使わない場合)
func CollectNames(users []User) []string {
names := make([]string, len(users))
for i, u := range users {
names[i] = u.Name
}
return names
}

注意点

  • preallocmake([]T, 0, n) での事前割り当てを推奨するのに対し、makezero は make([]T, n)append の組み合わせを防ぐ。両者を併用すると効果的
  • always: true にするとインデックスアクセスのみの正当なケースも検出するため、厳格なスタイルを強制したい場合に使用する
  • // nozero コメントで特定行の警告を抑制できる

参考リンク