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

出典: フリー百科事典『ウィキペディア(Wikipedia)』
移動: 案内検索
Go
パラダイム コンパイラ言語並列プログラミング
登場時期 2009年 (2009)
設計者 ロバート・グリーセマー[1]ロブ・パイクケン・トンプソン
開発者 グーグル
最新リリース 1.3 / 2014年6月18日(4か月前) (2014-06-18
型付け 強い型付け
主な処理系 GC(C言語で記述。構文解析にyacc/bisonを使用)、Gccgo(再帰下降パーサを持つC++フロントエンド、バックエンドに標準GCC[2]
影響を受けた言語 C言語、LimboModulaNewsqueakOberonPascal[3]Python
プラットフォーム LinuxOS XFreeBSDWindows
ライセンス BSDライセンス
ウェブサイト golang.org

Goプログラミング言語のひとつ。グーグル社によって開発されており[4]、設計にロブ・パイクケン・トンプソンらが関わっている。

主な特徴として、軽量スレッディングのための機能、Pythonのような動的型付き言語のようなプログラミングの容易性、などがある。Go処理系としてはコンパイラのみが開発されている。

発表当初はLinuxMac OS Xのみしかサポートしていなかったが[5]、2012年3月にリリースされたバージョン1からはWindowsもサポートされている[6]。また、2011年5月10日に公開された Google App Engine 1.5.0 でも、Go言語がサポートされている[7]

特徴[編集]

Goの構文は様々な言語に部分的に類似している。変数の定義における型の記法はLimboと同様の後置でALGOLPascalに類似し、ブロックの区切りに波括弧を使う記法はC言語に類似している。forifでは条件式を丸括弧で括らず帰結部分には波括弧が必須である。メモリ管理はガベージコレクションに一任され、連想配列も備える。並列処理はOccamLimboと同様、アントニー・ホーアによるCSPのプロセス代数をモデルとし[3]Limboと同様チャンネルによるスレッド間通信機能がある。

型の継承ジェネリックプログラミングアサーションオーバーロードといった機能が存在しないことも特徴である。インターフェースを用いたポリモーフィズム(多態性)が実現されている。panicrecoverを用いた例外処理機能を提供している。[3]FAQにおいて、ジェネリックプログラミングは一部導入が表明されているが、オーバーロードは効率的見地から排除されたことが述べられている。関数は多値を返すことができるので、それによりエラーの報告は容易である、としている。

Hello World[編集]

package main
 
import "fmt"
 
func main() {
	fmt.Printf("Hello, World\n")
}

詳細[編集]

構造型 struct[編集]

Goにおいて構造型の定義は下記の様に記述する。

// 構造型の定義
type MapEntry struct {
    name string
    value int
}
 
// メソッドの定義
func (this *MapEntry) PrintName() {
	fmt.Printf("%s", this.name) ;
}
 
func (self *MapEntry) PrintValue() {
	fmt.Printf("%d", self.value) ;
}

メソッドは型定義の外に単一ディスパッチレシーバーによって記述される。レシーバーは func (変数名 型名) メソッド名 (パラメータ…) と記述する。レシーバー内の変数の変数名は自由に指定が可能であり、受け取り方も値渡し、ポインター渡しのどちらかで指定する。

Go では、構造型データとメソッドが切り離されており、データは一切メソッドに依存しない。メソッドとデータは、メソッドの追加対象となる型名をレシーバーの型名に指定して関連付ける。型からメソッドが切り離されているため型本体の変更無しでメソッド追加可能であり、同一パッケージ内限定ではあるが、開放された型を実現している。

Goには構造型データを初期化したり終了処理する機能が存在しない。初期化は型に所属しない関数や、他の型のメソッドで行う。

新しい基本型の定義[編集]

Goでは非構造型の型についても新しい型を定義できる。

次の例では、int型を元に MathInt という新しい型を定義している。:

type MathInt int

これは単に既存の型に別名を与えているのではなく元のint型と別の型を定義している点が大きく異なる。

新しく定義した型であれば、元の型が構造体型でなくともメソッドを追加することが出来る。例えばint型やマップ型といった事前定義済み型(組み込み型)に対してもメソッドを追加することが可能である。

事前定義済み型から新しい型を定義した場合、下記の様な記述が可能となる。:

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

インターフェイス型 interface[編集]

GoにはC++や他の言語における仮想関数を持つ型が存在しない。型に追加したメソッドは一種の非仮想関数である。Goにおいての多態はインターフェイス型を使用して実現する。

インターフェイス型は実装を一切持たずメソッドの形式だけを定義した型である。但し、インターフェイス型は代入できる型との関連付けが不要である。インフーフェイスに定義しているメソッドを全て持ってさえいればどんなオブジェクトでも代入可能である。

Goにおけるインターフェイスの例を下記に示す。:

type Container interface {
	Begin() Iterator
	End() Iterator
}

継承[編集]

Goは建前上、型継承の仕組みを持っていない。しかし、匿名フィールドというメンバーを定義する事で実質的に型継承を記述することが可能である。

Goにおける多重継承の例を下記に示す。:

type BaseA int
 
func (_ *BaseA) Function() {
}
 
func (_ *BaseA) FunctionA() {
}
 
type BaseB int
 
func (_ *BaseB) Function() {
}
 
func (_ *BaseB) FunctionB() {
}
 
type Derived struct {
	BaseA
	BaseB
}

呼び出し例:

var derived Derived
 
derived.FunctionA()
derived.FunctionB()

Goは構造体に変数名を省き型名だけ記述すことにより匿名フィールドを定義できる。匿名フィールドは派生型からセレクターによるメソッド呼び出しが暗黙に委譲される。基底となる型として機能するのである。ただし、派生型から暗黙に匿名フィールドと同じ型にキャストする事はできない。

上記の例に示した通りGoは実質的には多重継承が可能である。多重継承には基底型のメンバーが重複するという問題がつきまとう。Goにおいても同様で上記の例では derived.Function() という呼び出しができない。また、上記の例に登場する Derived のオブジェクトは Function() を持つインターフェイスに代入する事ができない。

Goは、この解決方法に次の2つの方法を提供している。

  1. 匿名フィールドを明示的に指定する
  2. 派生型にも同じメソッドを作り明示的に委譲する

1.の匿名フィールドを明示的に指定した例を下記に示す。:

var derived Derived
 
derived.BaseA.Function()
derived.BaseB.Function()

匿名フィールドを明示的に操作する場合にも同様の方法を使用する。

下記に例を示す。:

var derived Derived
var base *BaseA = &derived.BaseA
 
derived.BaseA = 100
derived.BaseA += 100
base.Function()

1.の方法はセレクターによるメソッドの呼び出しは解決できるもののインターフェイスへの代入については解決できない。インターフェイスへの代入まで可能にするには2.の方法で解決する。

2.の派生型で明示的に委譲する例を下記に示す。:

type Derived struct {
	BaseA
	BaseB
}
func (this *Derived) Function() {
	// 明示的にBaseAのFunction()に委譲する
	this.BaseA.Function()
}

上記の例を使用したインターフェイスへの代入例:

type Something interface {
	Function()
}
 
var derived Derived
var something Something = &derived
something.Function()

委譲による継承の特性[編集]

Goにおける継承はあくまで委譲の発展である。この方式は、元となる型としてあらゆる型を指定できるという特徴を持っている。構造体の派生型は勿論の事、型を修飾したポインター型、インターフェイスも匿名フィールドとして基底型代わりに使用出来る。この特徴によりBridgeパターンを簡単に実装でき、実装継承の問題の一つである基底型が固定される問題を手軽に回避できる。

下記にGoによるブリッジ・パターンの例を示す。:

type Base interface {
	Function()
}
 
type Derived struct {
	Base
}
 
func (this *Derived) WithBase (base Base) {
	this.Base = base
}

呼び出し例:

// 新しい実装の定義
type Implement int
func (_ Implement) Function() {
}
 
var derived Derived
var implement Implement
 
// 基底となる型の実装を置き換える
derived.WithBase (&implement)
// ImplementのFunction()が呼び出される
derived.Function()

アクセス指定子[編集]

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

資源開放[編集]

Goではdefer文という例外安全な強制実行のしくみを採用している。

deferdeferキーワードに続けて、関数呼び出しかセレクターによる呼び出しを記述することで関数終了時に指定した処理の呼び出しを実行する。

deferは下記の様に記述する。:

defer 関数呼び出しまたはセレクター呼び出し

deferはブロックを持たず入れ子が深くなるようなことはない。ただし、関数以外のブロックを無視する点に注意が必要である。例えばループ中でdeferを使用した場合、ループ内では指定した関数が呼ばれず関数終了時にdeferで指定した全ての関数呼び出しが行われる。

deferには、関数呼び出しかセレクターによる呼び出ししか記述できず、式や文を直接記述することはできないが、下記のように無名関数を利用して、任意の式や文の実行も可能である。:

defer func() {
	message := "error"
	fmt.Println( message )
}()

例外処理[編集]

冒頭でも述べたように、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は構文ではなく関数であるためdeferdefer recover() と直接記述することもできる。ただし、recoverが機能するのはdeferで呼び出した関数の内部だけであり、deferに直接記述した場合は機能しない。

recoverdeferで実行される関数内であればどこでも機能する。これを利用し例外が発生する関数に直接recoverを記述せず、例外処理用関数を用意してそこにrecoverを記述し、共通する例外処理を一つの関数にまとめる事もできる。処理をまとめることができるが記述が冗長となる。

Godeferpanic関数・recover関数の組み合わせでは、deferで起きたpanicrecoverで捕まえられる。

ダウンキャスト[編集]

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を使うことで[8]、対象の値型やその値などの詳細を得ることができる。

名前とモジュールの管理[編集]

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の名前管理の特筆する点として名前解決の方法として階層型の名前空間ではなく別名の仕組(のみ)を採用している点が挙げられる。名前解決をするために長い名前[9]を記述する煩わしさはない。

更にGoにはモジュール管理の点でも特筆すべき点がある。Goはソースファイル中に一つ初期化関数(init)を定義する事で、初期化関数により実行時に広域変数を初期化できるという機能を持っている。この初期化関数が呼ばれるソースファイルは、main関数の定義されたソースファイルから連鎖的にimportされたパッケージに含まれるソースファイルだけである。この特性により使う予定もないのに初期化コードが実行されるおそれはない。また、パッケージが初めて参照されるまで初期化が遅延されてしまうという事もない。

脚注[編集]

  1. ^ Robert Griesemer
  2. ^ http://golang.org/doc/go_faq.html#Implementation
  3. ^ a b c http://golang.org/doc/go_faq.html
  4. ^ Google-go-language
  5. ^ go installation guide
  6. ^ Go version 1 is released” (2012年3月28日). 2012年3月29日閲覧。
  7. ^ Google App Engine Blog, Tuesday, May 10, 2011
  8. ^ reflect - The Go Programming Language
  9. ^ 完全修飾名

関連項目[編集]

外部リンク[編集]