Go言語入門:言語仕様 -Vol.133-

スポンサーリンク
Go言語入門:言語仕様 -Vol.133- 用語解説
Go言語入門:言語仕様 -Vol.133-
この記事は約5分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

本日は、Go言語の言語仕様について解説しています。

スポンサーリンク

背景

Go言語を学び始めて、公式の「The Go Programming Language Specification(言語仕様書)」を開いてみたものの、「英語で書かれていて読むのが大変…」「専門用語ばかりで何を言っているのかわからない…」と感じたことはありませんか? 実は、多くのGo初心者が同じ壁にぶつかっています。

言語仕様書は、Go言語の「正式な取扱説明書」のような存在です。プログラミング言語がどのように動くのか、どんなルールで書くべきなのかが詳しく書かれていますが、その分、初めて読む人には難しく感じられるのも事実です。

そこでこの記事では、言語仕様書の導入部分を丁寧な日本語訳とともに、初心者の方でも理解しやすい補足説明を加えてお届けします。「強く型付けされている」「ガベージコレクション」「並行プログラミング」といった専門用語も、具体例を交えながらわかりやすく解説していきます。

言語仕様書は難しそうに見えますが、一つひとつの概念を丁寧に読み解いていけば、必ず理解できます。一緒に、Go言語の基礎をしっかり学んでいきましょう!

メモリ確保(Allocation)

組み込み関数 new は、新しい初期化された変数を作成し、それへのポインタを返す。単一の引数を受け取り、それは型または式のいずれかである。

引数が型 T の場合、new(T) はゼロ値に初期化された型 T の変数を確保する。

引数が式 x の場合、new(x)x の型の変数を x の値に初期化して確保する。その値が型なし定数の場合、まず暗黙的にそのデフォルト型に変換される。型なし真偽値の場合、まず暗黙的に bool 型に変換される。事前宣言された識別子 nilnew の引数として使用できない。

たとえば、new(int)new(123) はそれぞれ int 型の新しい変数へのポインタを返す。最初の変数の値は 0 であり、2番目の値は 123 である。同様に

type S struct { a int; b float64 }
new(S)

は型 S の変数を確保し、初期化(a=0b=0.0)して、その変数のアドレスを含む *S 型の値を返す。


解説

new ってなに?

new は、指定した型のゼロ値(または指定した値)を持つ変数をメモリに確保し、そのポインタを返す組み込み関数です。

p := new(int)      // *int 型のポインタ。*p == 0(ゼロ値)
fmt.Println(*p)     // 0

型を渡す場合と値を渡す場合

new には2つの使い方があります。

型を渡す:ゼロ値で初期化

p := new(int)       // *p == 0
q := new(string)    // *q == ""
r := new(bool)      // *r == false

値を渡す:その値で初期化

p := new(42)        // *p == 42、型は *int
q := new("hello")   // *q == "hello"、型は *string

値を渡す形式は、「値へのポインタをその場で作りたい」ときに便利です。

new vs & vs make

Go にはメモリを確保する方法が複数あるので、使い分けを整理しておきましょう。

new(T) :ゼロ値へのポインタ

p := new(int)   // *int、値は 0

&T{} :複合リテラルのアドレス(構造体で多用)

p := &Point{X: 1, Y: 2}   // *Point、値を指定できる

make(T, ...) :スライス、map、チャネル専用

s := make([]int, 5)         // []int(ポインタではなく値を返す)
new&T{}make
返す型*T*TT
対象任意の型複合リテラルのある型スライス、map、チャネル
初期値ゼロ値(または指定値)リテラルで指定内部構造を初期化

実際のコードでの使われ方

正直なところ、new は Go のコードではあまり頻繁には使われません。構造体の場合は &T{} のほうが値を指定しながらポインタを作れるので便利です。

// new を使う(ゼロ値のみ)
p := new(Point)          // &Point{X: 0, Y: 0}

// &T{} を使う(値を指定できる)
p := &Point{X: 1, Y: 2}  // こちらのほうが柔軟

new が活きる場面は、基本型(intbool など)のポインタが必要なときです。

// API が *bool を要求する場合
func setFlag(b *bool) { ... }

// &true とは書けない(リテラルのアドレスは取れない)
// new を使うか...
p := new(true)   // Go の新しい仕様で値を渡せる

// ヘルパー関数を使うパターンも一般的
func boolPtr(b bool) *bool { return &b }
setFlag(boolPtr(true))

nil は渡せない

p := new(nil)  // コンパイルエラー!

nil は型情報を持たないので、new がどの型の変数を確保すればよいかわかりません。

new で確保した変数はどこにある?

Go では、new で確保した変数がスタック上に置かれるかヒープ上に置かれるかは、コンパイラが自動的に判断します(エスケープ解析)。プログラマが意識する必要はありません。

func f() *int {
    p := new(int)  // ヒープに確保される(関数を抜けても使われるため)
    return p
}

func g() {
    p := new(int)  // スタックに確保される可能性がある(関数内でしか使わないため)
    *p = 42
    fmt.Println(*p)
}

C 言語のように「malloc で確保したら free で解放する」といった手動のメモリ管理は不要です。使われなくなった変数はガベージコレクタが自動的に回収してくれます。

おわりに 

本日は、Go言語の言語仕様について解説しました。

よっしー
よっしー

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました