Go (プログラミング言語)

名称

しばしばGolangやGo言語と表記されることがあるが、言語の正式名称はGoである。golangという通称は、公式サイトのドメインであるgolang.orgに由来するものである。このドメインになったのはgo.orgが取得できなかったからであるとされている。
また、公式のロゴではすべて大文字でGOと描かれているが、言語の名前としてはGoであるとされている。

Hello world

次のコードはGoで書いたHello worldプログラムである。
package mainimport "fmt"func main() {
    fmt.Println("Hello, World")}

並行性の例

次のサンプルプログラムは、Goの並行性機能をデモンストレーションする非同期プログラムの実装である。2つの"goルーチン"（軽量スレッド）を立ち上げている。一方はユーザーがテキストを入力するのを待機し、他方はタイムアウトを実現する。select文が2つのgoルーチンがメインルーチンにメッセージを送信するのを待機し、最初に到達したメッセージに対して動作を実行する（コード例はDavid Chisnallの本のコードを一部修正している）。
package mainimport (
    "fmt"
    "time")func readword(ch chan string) {
    fmt.Println("Type a word, then hit Enter.")
    var word string
    fmt.Scanf("%s", &word)
    ch <- word}func timeout(t chan bool) {
    time.Sleep(5 * time.Second)
    t <- true}func main() {
    t := make(chan bool)
    go timeout(t)

    ch := make(chan string)
    go readword(ch)

    select {
    case word := <-ch:
        fmt.Println("Received", word)
    case <-t:
        fmt.Println("Timeout.")
    }}

文法

構文は様々な言語に部分的に類似している。変数などにおける型の記法はLimboから引き継いだものと思われる、型名を後置するもので、PascalやAdaに類似している。ブロックの区切りに波括弧を使う記法はC言語他多くと同様である。for文やif文では、条件式を丸括弧で括らず、帰結部分には波括弧が必須である。他に、並列処理について、CSPを参考としている、チャネルによるスレッド間通信機能の構文がある。

構造体

Goには構造体という概念があり、実装は下記の様に記述する。
// 型(構造体の場合)の定義type MapEntry struct {
    name  string
    value int}// メソッドの定義func (m *MapEntry) PrintName() {
fmt.Printf("%s", m.name)}func (m *MapEntry) PrintValue() {
fmt.Printf("%d", m.value)}
コード例から分かる通り、構造体には型名、フィールド、メソッドが定義されている。GoにはJavaのような言語が持つクラスやオブジェクトのような概念は無いが実質的には構造体はそれらに近い役割を提供する。
Goでは、関数に(m *MapEntry)のようなレシーバーの宣言を追加してメソッドを定義する。レシーバーの宣言は (変数名 型名) と記述する。レシーバー内の変数名は自由に指定が可能であり、受け取り方は値渡し、ポインター渡しの2種類から指定出来る。
型とメソッドが切り離されており、型は一切メソッドに依存しない。メソッドと型は、メソッドの追加対象となる型名をレシーバーの型名に指定することで関連付けるようになっている。メソッドは型から切り離されているため型本体を変更する事なくメソッドを追加することが可能であり、同一パッケージ内限定ではあるが、開放された型を実現している。
Goには型を初期化したり終了処理する機能が存在しない。初期化は型に所属しない関数や、他の型のメソッドで行うようになっている。

新しい型の定義

GoではC++のtypedefのように新しい型を定義できる。
次の例では、int型を元に MathInt という新しい型を定義している。
type MathInt int
これは単に既存の型に別名を与えているのではなく元のint型と別の型を定義している点が大きく異なる。
新しく定義した型であれば、元の型が構造体型でなくともメソッドを追加することが出来る。例えばint型やマップ型といった事前定義済み型(組み込み型)に対してもメソッドを追加することが可能である。
事前定義済み型から新しい型を定義した場合、下記の様な記述が可能となる。
func main() {
var value MathInt

value = -10
fmt.Printf("%d", value.Abs()) // -10の絶対値を表示する}

匿名フィールド

Goには継承が意図的に実装されていない。しかし匿名フィールドという機能を用いることで、ある構造体と同じフィールド、メソッドを持つ構造体を容易に作成することができる。
type Base intfunc (_ *Base) Function() {}type Derived struct {
  Base}// 呼び出し例var derived Derivedderived.Function()
このようにして作られた構造体から匿名フィールドで用いられた元となる構造体の型に暗黙的にキャストするような機能は存在していない。

interface

GoにはC++や他の言語における仮想関数を持ったクラスが存在しない。型に追加したメソッドは一種の非仮想関数である。Goにおいての多態はinterface型を使用して実現する。
interface型は実装を一切持たずメソッドの形式だけを定義した型であり、その点はC#など最近の言語のinterfaceと同じである。但し、Goのinterface型は、代入できる型との関連付けが不要である。interfaceに定義しているメソッドを全て持っていればどんなオブジェクトでも代入可能である。
Goにおけるinterfaceの例を下記に示す。
type Container interface {
Begin() Iterator
End() Iterator}
Goのinterfaceは、MLのsignatureや、かつてg++に存在したsignatureとC++拡張と全く同じ機能である。

defer

Goではdefer文という例外安全な強制実行のしくみを採用している。
deferはdeferキーワードに続けて、関数呼び出しかセレクターによる呼び出しを記述することで関数終了時に指定した処理の呼び出しを実行する。
deferは下記の様に記述する。
defer 関数呼び出しまたはセレクター呼び出し
deferはブロックを持たず入れ子が深くなるようなことはない。ただし、関数以外のブロックを無視する点に注意が必要である。例えばループ中でdeferを使用した場合、ループ内では指定した関数が呼ばれず関数終了時にdeferで指定した全ての関数呼び出しが行われる。
deferには、関数呼び出しかセレクターによる呼び出ししか記述できず、式や文を直接記述することはできないが、下記のように無名関数を利用して、任意の式や文の実行も可能である。
defer func() {
message := "error"
fmt.Println( message )}()

例外処理

他の言語で使われているtry-catch方式の例外処理機能はないが代わりの機能としてpanicとrecoverを用いた例外処理機能を提供している。Goは一般的な例外処理への使用を推奨しないものの、例外処理用の専用構文を備えている。一般的な例外にはあくまで戻り値を使う。
例外を陽に発生させる仕組みとしてpanic関数がある。
panic は下記のように記述する
panic( /* 各種値 */ )
panic関数の引数には、数値や関数、構造体といった各種値を指定する事が出来る。特定型のデータでなければ使えないといった制限はない。また、panicは構文ではなく関数である。有用性は無いが、deferなど式が使えず関数呼び出ししか記述できない場所でも直接記述できる。
panic関数を実行するとpanic呼び出し以降の処理を中断し、コールスタックを巻き戻す。 ただし、panic呼び出し以前にdeferされた関数やセレクターについては呼び出しが実行される。
panic関数で投じられた例外データは、recover関数と、deferを組み合わせて受け取る。recover関数は、panic関数の引数を返し、コールスタックの巻き戻しを停止する。
recoverの使用例を下記に示す。
func Example() {
defer func() {
/* recoverを呼んだ時点でコールスタックの巻き戻しが終了する */
cause := recover()
fmt.Fprintln (os.Stderr, cause)
}()

panic ("error") ;
/* panic以降Example内の処理は実行されない */}func main() {
Example()
/* Example()の呼び出し側はrecover関数が呼び出されたため処理を継続できる */}
recoverは構文ではなく関数であるためdeferに defer recover() と直接記述することもできる。ただし、recoverが機能するのはdeferで呼び出した関数の内部だけであり、deferに直接記述した場合は機能しない。
recoverはdeferで実行される関数内であればどこでも機能する。これを利用し例外が発生する関数に直接recoverを記述せず、例外処理用関数を用意してそこにrecoverを記述し、共通する例外処理を一つの関数にまとめる事もできる。処理をまとめることができるが記述が冗長となる。
Goのdeferとpanic関数・recover関数の組み合わせでは、deferで起きたpanicもrecoverで捕まえられる。

ダウンキャスト

Goはインターフェース型の値から、基底型の値（インターフェース型に変換される前の型の値）を動的に安全に得るダウンキャストの仕組みとして「型アサーション」と呼ばれる機能を備えている。
// Value の返値はどんな型でも構わないvar i interface{} = Value()/*    i の基底型を string と仮定し、    実際にそうであれば s には変換結果が、 ok には true がセットされ、    そうでなければ s にはstring型の初期値（ゼロ値）が、 ok には false がセットされる。    以下の様に2つ目の返値を無視すると、i の基底型が string でなければ暗黙的にランタイムパニックとなる    s := i.(string)*/if s, ok := i.(string); ok {
    fmt.Println ("文字列: ", s)} else {
    var kind string

    // 型switch
    switch i.(type) {
    case int:
        kind = "数値"
    default:
        kind = "その他"
    }

    fmt.Printf ("%s: %v\n", kind, i)}
標準パッケージのreflectを使うことで、対象の値型やその値などの詳細を得ることができる。

名前とモジュールの管理

Goには名前とモジュールを管理するために、パッケージという仕組みを持っている。
基本的なパッケージの使用例を下記に示す。
package main// 現在のソースファイルが所属するパッケージ名の指定
// 本記事の例では省略されている事が多いが本来は必須である
 
import _ "./sub/a"// (1)ソースファイル中から名前が使用されないパッケージの取り込みimport . "./sub/b"// (2)パッケージ名を省略して、パッケージ内の名前を現在のソースファイルに全て取り込むimport alias_c "./sub/c"// (3)パッケージ名に別名を付けてパッケージ名を現在のソースファイルに取り込むimport "./sub/d"// (4)パッケージ名に末端のパッケージ名と同じ名前を付けて現在のソースファイルに取り込むfunc main() {
Function()// (2)のパッケージに所属するFunctionの呼び出し
alias_c.Function()// (3)のパッケージに所属するFunctionの呼び出し
d.Function()// (4)のパッケージに所属するFunctionの呼び出し}
Goには多彩なimport文が存在するが、その構成要素は、別名の指定とGoのソースを格納したフォルダーの指定（またはパッケージバイナリー）という非常に単純なものである。Goでは名前管理とモジュールの取り込み指定を一つの構文にまとめる事で、他の言語では煩わしいモジュールの結合を簡潔なものとしている。
Goの名前管理の特筆する点として名前解決の方法として階層型の名前空間ではなく別名の仕組（のみ）を採用している点が挙げられる。名前解決をするために長い名前を記述する煩わしさはない。
更にGoにはモジュール管理の点でも特筆すべき点がある。Goはソースファイル中に一つ初期化関数（init）を定義する事で、初期化関数により実行時に広域変数を初期化できるという機能を持っている。この初期化関数が呼ばれるソースファイルは、main関数の定義されたソースファイルから連鎖的にimportされたパッケージに含まれるソースファイルだけである。この特性により使う予定もないのに初期化コードが実行されるおそれはない。また、パッケージが初めて参照されるまで初期化が遅延されてしまうという事もない。

アクセス指定子

Go には、型レベルのアクセス指定子が存在しないが、パッケージレベルでのアクセス制御が存在する。パッケージレベルでのアクセス制限はシンボル名（変数名、メソッド名、関数など）の先頭一文字目が大文字か小文字かで決定する。大文字であればパッケージに対し公開され、小文字であれば非公開となる。

提供しない機能

型の継承、ジェネリックプログラミング、アサーション、オーバーロードといった機能をあえて提供していない。FAQにおいて、ジェネリックプログラミングは一部導入が表明されているが、オーバーロードは効率的見地から排除されたことが述べられている。関数は多値を返すことができるので、それによりエラーの報告は容易である、としている。

処理系

Go言語の処理系は2種類ある。
gcはGo言語で実装されたセルフホスティングのGo言語コンパイラである。バージョン1.4以前はC言語で実装されていたが、バージョン1.5でC言語からGo言語への変換ツールでコンパイラの実装言語を切り替えて、以降はGo言語で開発している。構文解析にyacc・bisonを使用している。
Gccgoは、再帰下降パーサを持つC++フロントエンド、バックエンドに標準GCCを利用したサードパーティー開発のGo言語コンパイラである。