blog logo

Goのnew関数とmake関数の違いを解説する

thumnail
2019-12-27

new関数とmake関数。どちらもメモリ領域を確保する関数であるということはわかるのだが、実際にどのように使い分ければ良いのか?という点がわからなかった。この点を調べたのでこの記事で言語化する。

new関数

無名変数を生成する

Goのnew関数を使う場合、以下のように使用する。

new(T)

Tは型を指しており、int型やstring型などの基本型を引数として渡す。new関数の特徴として、new関数はゼロ値が格納されたアドレスを返す。次のプログラムを見て欲しい。

v := new(int)
fmt.Println(v)  // 0xc0000160c8
fmt.Println(*v) // 0

Tに該当する部分をint型として指定した。出力を確認すると、*v(アドレスのが指定する主記憶の番地の値を参照)でゼロ値が参照されることがわかる。(int型のゼロ値は0) またvをそのまま出力するとアドレスを参照していることが確認できる。無名変数というのはメモリのある位置を確保して、その中に値を格納できる状態にしただけの状態のことを指す。

ポインタ型をTに指定するとどうなるのか

Tを*int、すなわちintのポインタ型を指定した場合どのような結果になるのかを確認してみる。以下のプログラムを見てほしい。

v := new(*int)
fmt.Println(v)  // 0xc0000160c8
fmt.Println(*v) // <nil>

ポインタ型を指定した場合でもnew関数はアドレスを返す。int型を引数に取った場合との違いはアドレス先に格納されている値が0かnilかという点である。しかしこれはint型のゼロ値は0、ポインタのゼロ値はnilの違いでしかない。

make関数

初期化された領域への参照を返す

make関数は初期化された領域への参照を返す。make関数が対応する型はslice,map,chanelのみ。他の型はmake関数では扱うことができない。次のコードを見て欲しい。

m := make([]int, 1)
fmt.Println(m) // [0]
m[0] = 1
fmt.Println(m) // [1]

このコードは長さ1のスライスを確保したコードだ。make関数の第2引数にはスライスの長さを指定できる。スライスの初期値はnilであるがmake関数でスライスを生成したため、初期値としてintの0が格納されていることがわかる。第3引数には容量を指定することができる。

map型における挙動

mapの初期値はnilだ。map型は初期化されていない場合、値を代入することができない。次のコードを見て欲しい。

var m map[string]bool
fmt.Println(m)    // map[]
m["hoge1"] = true // error : assignment to entry in nil map
fmt.Println(m)

代入しようとしたところ、nilのmapには代入することができないと言われている。これはmapのゼロ値はnilだからだ。

var m map[string]int
fmt.Println(m == nil) // true

make関数を使った場合は

m := make(map[string]bool)
fmt.Println(m)    // map[]
m["hoge1"] = true // map[hoge1:true]
fmt.Println(m)

map型は初期値で初期化されているため、キーを指定して、値を代入することができる。

まとめ

  • new関数はゼロ値で初期化する。make関数は初期値を設定する
  • new関数はアドレスを返す
  • new関数は全ての型を受けつける。make関数はコンポジット型のみ
profile

KATUO

web developer

六本木のミドルベンチャーでwebエンジニアをやってます。普段はフロントからサーバーサイド、インフラ周りといった領域を幅広く触ってます。

twitter-icongithub-icon

KATUBLO

Copyright since 2018 KATUO All Rights Reserved.