はじめに
Go言語でメモリを確保する際に使われるnew
とmake
。どちらも初学者には混同しがちですが、それぞれの役割は大きく異なります。本記事では、それぞれの機能や使い分けについて、具体例を交えて分かりやすく解説します。
new
とmake
の基本的な違い
new
- 目的: メモリを割り当て、ゼロ値で初期化する。
- 戻り値: 割り当てられたメモリへのポインタ。
-
対象: 値型(例:
int
,struct
など)。
new
は単純にメモリを確保するだけで、複雑な初期化処理は行いません。
package main
import "fmt"
func main() {
p := new(int) // *int型のメモリを確保
fmt.Println(*p) // => 0 (ゼロ値)
*p = 42 // 値を代入
fmt.Println(*p) // => 42
}
make
- 目的: スライス、マップ、チャネルを初期化する。
- 戻り値: 初期化されたスライス、マップ、またはチャネル(ポインタではない)。
- 対象: 参照型(スライス、マップ、チャネル)。
make
は、これらの参照型を初期化し、利用可能な状態にします。
package main
import "fmt"
func main() {
s := make([]int, 3) // 長さ3のスライスを作成
fmt.Println(s) // => [0 0 0]
m := make(map[string]int) // 空のマップを作成
m["key"] = 100
fmt.Println(m) // => map[key:100]
}
使い分けのポイント
new
とmake
は役割が明確に分かれているため、使い分けを覚えるのは簡単です。
-
値型や構造体を初期化する場合 →
new
を使用- ポインタが必要な場面で便利。
-
struct
の初期化にも使えるが、通常はリテラルの方が簡潔。
type Person struct {
Name string
Age int
}
func main() {
p := new(Person) // *Person型
fmt.Println(p.Name) // => "" (ゼロ値)
p.Name = "Alice"
fmt.Println(p) // => &{Alice 0}
}
-
スライス、マップ、チャネルを初期化する場合 →
make
を使用-
make
が必要なのはこれらの参照型だけ。 - 初期化されていないスライス、マップ、チャネルは
nil
であり、操作するとパニックが発生する。
-
func main() {
var m map[string]int // nilのマップ
// m["key"] = 1 // パニック発生
m = make(map[string]int)
m["key"] = 1 // 正常に動作
fmt.Println(m) // => map[key:1]
}
注意点とベストプラクティス
-
構造体の初期化はリテラルを優先
new
を使うよりも、構造体リテラルを使う方が読みやすく、簡潔です。
p := &Person{Name: "Alice", Age: 30} // 推奨
-
参照型は必ず
make
で初期化
特にマップやチャネルでは、make
を使わずに操作しようとするとエラーが発生します。 -
パフォーマンスを意識した選択
- スライスの容量を明示的に設定する場合は
make
を使うことで効率的なメモリ割り当てが可能です。
- スライスの容量を明示的に設定する場合は
new
とmake
の違いをまとめる
特徴 | new | make |
---|---|---|
対象 | 値型、構造体 | スライス、マップ、チャネル |
戻り値 | ポインタ | 初期化済みのオブジェクト |
初期化の深さ | ゼロ値のみ | 完全な初期化 |
主な用途 | メモリ割り当てとゼロ値 | 参照型の初期化 |
おわりに
new
とmake
の違いを正しく理解すれば、Go言語のメモリ管理を効率的に行うことができます。本記事を参考に、それぞれの関数を適切に使い分けて、より明確で安全なコードを書いていきましょう!