はじめに
今回、Go言語のnew
関数とmake
関数の違いについて解説していこうと思います。
この2つの関数は似ているので、初学者の方には混乱を招くことがあります。
本記事で、2つの特徴を掴み理解を深めていきましょう。
まずは、それぞれの関数の解説から始めます。
new関数 について
-
new関数
- 指定された型の新しいインスタンスをメモリ上に割り当て、そのポインタを返す関数
- new関数によって割り当てられた値は、その型のゼロ値で初期化されます
実際にnew関数は以下のように使用できます。
package main
import "fmt"
func main() {
var p *int = new(int)
fmt.Println(p) //出力:0x1400000e0a8
}
アドレスが確保されていることが確認できたかと思われます。
では、new
関数を使わない場合も考えてみましょう。
package main
import "fmt"
func main() {
var p *int = new(int)
fmt.Println(p) //出力:0x1400000e0a8
// p2というポインタ型を宣言
var p2 *int
fmt.Println(p2) //出力: <nil>
}
The Go Playground:実行してみましょう。
上記のことから以下の点がわかります。
-
new
関数はメモリ領域を確保してくれるため、アドレスが返ってくる - 変数
p2
はメモリを確保していないためnil値が出力された
変数p
にはデフォルトで「0」が入っています。
func main() {
var p *int = new(int)
fmt.Println(*p)
// 1インクリメントしてみる
*p++
fmt.Println(*p)
}
The Go Playground:実行してみましょう。
実行してみると、デフォルト値「0」に+1され「1」が返ってきてますね。
こちらの操作を先ほどの変数p2
にも行ってみましょう。
var p2 *int
*p2++
fmt.Println(*p2)
The Go Playground:実行してみましょう。
こちら実行すると「panic」が発生します。
エラー内容を見てみると以下のように書いてあります。
invalid memory address or nil pointer dereference
まだメモリに値が確保されていないので、その中身にアクセスしようとしたためpanicになったことが確認できます。
- new関数と普通の宣言は異なることなので区別しておきましょう!
- 構造体の場合はnew関数を使うよりも、構造体リテラルを使用してインスタンスを直接初期化する方が一般的です
続いて、make関数について解説していきます。
make関数 について
- slice, map, channelといったビルトインの参照型のために使用されます
- 新しいインスタンスを作成し、その初期化を行います
実際にコードを書いていきましょう。
sliceの初期化
- make関数を使ってスライスを初期化する際には、スライスの型、長さ、容量を指定する必要があります
- 容量については省略することができますが、その場合は容量には長さと同じ値が設定されます
//長さ10, 容量15のスライスを作成
s := make([]int, 10, 15)
mapの初期化
- マップを初期化する際はmake関数にキーとバリューの型を渡す必要があります
// キーがstring型、バリューがint型のmapを作成
m := make(map[string]int)
channelの初期化
- チャネルは、ゴルーチン間でのデータの受け渡しに使用されます
- チャネルの初期化はチャネルの型とバッファサイズを指定することができます
- バッファサイズを指定しない場合は非バッファチャネルが作成されます
// int型のバッファサイズ5のchannelを作成
ch := make(chan int, 5)
new関数とmake関数の違い
まずは以下のコードを見てみましょう。
package main
import (
"fmt"
)
func main() {
// スライスを作成し、その型を表示
s := make([]int, 0)
fmt.Printf("%T\n", s)
// マップを作成し、その型を表示
m := make(map[string]int)
fmt.Printf("%T\n", m)
// チャネルを作成し、その型を表示
ch := make(chan int)
fmt.Printf("%T\n", ch)
// new関数を使ってゼロ値で初期化されたインスタンスのポインタを返却
var st = new(int)
fmt.Printf("%T\n", st)
// ポインタを作成し、その型を表示
var p *int = new(int)
fmt.Printf("%T\n", p)
}
The Go Playground:実行してみましょう。
実行してみると struct
と変数 p
だけが、ポインタ型で返ってきていることがわかるかと思います。
つまり、
「ポインタが返ってくる返り値のものにはnew関数
を使い、そうでない場合はmakeを使います」
より詳細に説明すると、以下のように言えます。
- make関数:初期化済みのインスタンスを直接返却
- new関数:ゼロ値で初期化された指定された型の新しいインスタンスのポインタを返却
さいごに
本記事では、Go言語のnew関数とmake関数の違いについて詳しく解説しました。これらの関数の特徴や使用方法を正しく理解することで、より効果的なコードを書いていきましょう!
参考