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

提供: miniwiki
移動先:案内検索
Go
登場時期 2009年11月10日 (2009-11-10)
設計者 Robert Griesemer、ロブ・パイクケン・トンプソン
開発者 Google
最新リリース 1.10.3/ 2018年6月5日(5年前) (2018-06-05[1]
型付け 強い静的型推論構造的English版[2][3]
主な処理系 gc、gccgo
影響を与えた言語 CrystalEnglish版
テンプレートを表示

Goプログラミング言語の1つである。2009年、Google[4]Robert Griesemer、ロブ・パイクケン・トンプソンによって設計された[5]。Goは、静的型付けC言語の伝統に則ったコンパイル言語メモリ安全性English版ガベージコレクション構造的型付けEnglish版CSPスタイルの並行性などの特徴を持つ[6]。Goのコンパイラ、ツール、およびソースコードは、すべてフリーかつオープンソースである[7]

また、軽量スレッディングのための機能、Pythonのような動的型付け言語のようなプログラミングの容易性、などの特徴もある。Go処理系としてはコンパイラのみが開発されている。マスコット・キャラクターはGopher(ホリネズミ)。

発表当初はLinuxMac OS Xのみしかサポートしていなかったが[8]、2012年3月にリリースされたバージョン1.0からはWindowsもサポートされている[9]。2014年12月にリリースされたバージョン1.4からAndroidをサポートし[10]、2015年8月19日にリリースされたバージョン1.5からiOSをサポートしている[11]。また、2011年5月10日に公開された Google App Engine 1.5.0 でも、Go言語がサポートされている[12]

歴史

ファイル:Golang.png
現在のマスコットGopher。以前はGoのロゴとして使用されていた。

2009年11月にGoが初めて発表され[13]、バージョン1.0が2012年3月28日にリリースされた[14]。GoはGoogleの本番システムの一部で使用されており、他の多数の企業やオープンソースプロジェクトでも使用されている[15]

主に次の2つの実装が存在する。

  • GoogleのGoツールチェイン。複数のプラットフォームをサポートとしており、LinuxBSDmacOSPlan 9Windows、そして(2015年から)モバイルデバイスで動作する[16]。このGoコンパイラは、バージョン1.5からセルフホスティングされるようになった[17]
  • 2つ目のコンパイラgccgo。これは、GCCのフロントエンドである[18]

Goの起源はGoogleのエンジニアであるRobert Griesemer、ロブ・パイクケン・トンプソンによる、新しいプログラミング言語を設計する実験的なプロジェクトに端を発する。他の言語がよく受ける批判を解決するとともに、それらの言語のよい特徴をできる限り損なわないようにすることを目指した。彼ら設計者たちは、新しい言語として、以下の特徴を持つものを構想していた[19]

後のインタビューで、3人の言語設計者すべてが、新しい言語を設計する主なモチベーションとしてC++が好きでないことEnglish版を共有していたことを述べている[21]

バージョン1.6から、64ビットMIPS上で動作するLinux版および32ビットx86上で動作するAndroid版ポートが追加された[22]

2017年7月13日、バージョン2への取り組みへの開始が発表された[23]。バージョン1.x系とバージョン2の開発は並行して行われ、バージョン1.20がバージョン2と同義となると宣言されている。

2018年4月、旧ロゴ(Gopherマスコット)がテキストと3本の直線からなる新しいデザインのものに変更された。しかし、マスコットはそのまま変わっていない。

コード例

Hello world

次のコードはGoで書いたHello worldプログラムである。

package main

import "fmt"

func main() {
    fmt.Println("Hello, World")
}

平行性の例

次のサンプルプログラムは、Goの並行性機能をデモンストレーションする非同期プログラムの実装である。2つの"goルーチン"(軽量スレッド)を立ち上げている。一方はユーザーがテキストを入力するのを待機し、他方はタイムアウトを実現する。テンプレート:Mono文が2つのgoルーチンがメインルーチンにメッセージを送信するのを待機し、最初に到達したメッセージに対して動作を実行する(コード例はDavid Chisnallの本のコードを一部修正している)[24]:152

package main

import (
    "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から引き継いだものと思われる、型名を後置するもので、PascalAdaに類似している。ブロックの区切りに波括弧を使う記法はC言語他多くと同様である。for文if文では、条件式を丸括弧で括らず、帰結部分には波括弧が必須である。他に、並列処理について、CSPを参考としている[25]チャネルによるスレッド間通信機能の構文がある。

構造体

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 int

func (_ *Base) Function() {
}

type Derived struct {
  Base
}

// 呼び出し例
var derived Derived
derived.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文という例外安全な強制実行のしくみを採用している。

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

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

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

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

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

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

例外処理

他の言語で使われているtry-catch方式の例外処理機能はないが代わりの機能としてpanicrecoverを用いた例外処理機能を提供している。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を使うことで[26]、対象の値型やその値などの詳細を得ることができる。

名前とモジュールの管理

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

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

アクセス指定子

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

提供しない機能

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

処理系

Go言語の処理系は2種類ある[28]

gcはGo言語で実装されたセルフホスティングのGo言語コンパイラである。バージョン1.4以前はC言語で実装されていたが、バージョン1.5でC言語からGo言語への変換ツールでコンパイラの実装言語を切り替えて、以降はGo言語で開発している。構文解析にyaccbisonを使用している。

Gccgoは、再帰下降パーサを持つC++フロントエンド、バックエンドに標準GCCを利用したサードパーティー開発のGo言語コンパイラである。

脚注

  1. Release History - The Go Programming Language”. . 2018閲覧.
  2. Why doesn't Go have "implements" declarations?”. golang.org. . 1 October 2015閲覧.
  3. Pike, Rob (2014年12月22日). “Rob Pike on Twitter”. . 2016閲覧. “Go has structural typing, not duck typing. Full interface satisfaction is checked and required.”
  4. Kincaid, Jason (2009年11月10日). “Google’s Go: A New Programming Language That’s Python Meets C++”. TechCrunch. https://techcrunch.com/2009/11/10/google-go-language/ . 18 January 2010閲覧. 
  5. Language Design FAQ”. golang.org (2010年1月16日). . 27 February 2010閲覧.
  6. Metz, Cade (2011年5月5日). “Google Go boldly goes where no code has gone before”. The Register. . 2018閲覧.
  7. golang.org
  8. go installation guide
  9. Go version 1 is released” (2012年3月28日). . 2012閲覧.
  10. プログラミング言語「Go 1.4」がリリース、Androidを正式にサポート” (2014年12月12日). . 2015閲覧.
  11. Go 1.5 is released” (2015年8月19日). . 2015閲覧.
  12. Google App Engine Blog, Tuesday, May 10, 2011
  13. Hey! Ho! Let's Go!”. Google Open Source. Google. . 17 May 2018閲覧.
  14. Gerrand. “Go version 1 is released - The Go Blog”. . 2018閲覧. Shankland, Stephen (2012年3月30日). “Google's Go language turns one, wins a spot at YouTube: The lower-level programming language has matured enough to sport the 1.0 version number. And it's being used for real work at Google.”. CBS Interactive Inc (2012-03-30発行). https://www.cnet.com/news/googles-go-language-turns-one-wins-a-spot-at-youtube/ . 2017閲覧.. "Google has released version 1 of its Go programming language, an ambitious attempt to improve upon giants of the lower-level programming world such as C and C++."  Release History”. . 2018閲覧.
  15. “Go FAQ: Is Google using Go internally?”. http://golang.org/doc/faq#Is_Google_using_go_internally . 2013閲覧. 
  16. Google's In-House Programming Language Now Runs on Phones”. wired.com (2015年8月19日). . 2018閲覧.
  17. Go 1.5 Release Notes”. . 28 January 2016閲覧. “The compiler and runtime are now implemented in Go and assembler, without C.” Go 1.5が登場、Androidに続いてiOSアプリも開発可能。ガベージコレクションも大幅に改善” (2015年8月24日). . 2015閲覧.
  18. FAQ: Implementation”. golang.org (2010年1月16日). . 2010閲覧. Installing GCC: Configuration”. . 2011閲覧. “Ada, Go and Objective-C++ are not default languages”
  19. Pike, Rob (2010年4月28日). “Another Go at Language Design”. Stanford EE Computer Systems Colloquium. Stanford University. . 2018-7-24閲覧. Video available.
  20. Frequently Asked Questions (FAQ) - The Go Programming Language”. golang.org. . 2016閲覧.
  21. Andrew Binstock (2011年5月18日). “Dr. Dobb's: Interview with Ken Thompson”. . 2014閲覧. Pike, Rob (2012年). “Less is exponentially more”. . 2018閲覧. Robert Griesemer (2015年). “The Evolution of Go”. . 2018閲覧.
  22. 末岡洋子 (2016年2月19日). “「Go 1.6」リリース、細かな性能改善などがメインのリリースに”. . 2016閲覧.
  23. Cox, Russ. “Toward Go 2 - The Go Blog”. . 2018閲覧.
  24. Chisnall, David (2012). The Go Programming Language Phrasebook. Addison-Wesley. 
  25. 25.0 25.1 http://golang.org/doc/go_faq.html
  26. reflect - The Go Programming Language
  27. 完全修飾名
  28. http://golang.org/doc/go_faq.html#Implementation

関連項目

外部リンク



テンプレート:ロブ・パイク テンプレート:ケン・トンプソン