Go (プログラミング言語)
Goのロゴ | |
パラダイム | 構造化プログラミング、手続き型プログラミング、命令型プログラミング、並行計算、オブジェクト指向プログラミング、関数型プログラミング |
---|---|
登場時期 | 2009年11月10日 |
設計者 | Robert Griesemer、ロブ・パイク、ケン・トンプソン |
開発者 | |
最新リリース | 1.23.3[1] / 2024年11月6日 |
型付け | 強い、静的、型推論、構造的[2][3] |
主な処理系 | gc、gccgo |
影響を受けた言語 | C言語、Oberon-2、Limbo、Active Oberon、Communicating Sequential Processes、Pascal、Oberon-2、Smalltalk、Newsqueak、Modula-2、Alef、APL、BCPL、Modula、Occam |
影響を与えた言語 | Crystal |
プラットフォーム | DragonFly BSD、FreeBSD、Linux、macOS、NetBSD、OpenBSD、Plan 9 from Bell Labs、Solaris、Microsoft Windows、iOS、Android、AIX、illumos |
ライセンス | BSDライセンス |
ウェブサイト | |
拡張子 | go |
Goはプログラミング言語の1つである。Googleにおいて2009年[4]ロバート・グリースマ、ロブ・パイク、ケン・トンプソンによって設計された[5]。Goは、静的型付け、C言語の伝統に則ったコンパイル言語、メモリ安全性、ガベージコレクション、構造的型付け、CSPスタイルの並行性などの特徴を持つ[6]。Goのコンパイラ、ツール、およびソースコードは、すべてフリーかつオープンソースである[7]。
また、軽量スレッディングのための機能、Pythonのような動的型付け言語のようなプログラミングの容易性、などの特徴もある。Go処理系としてはコンパイラのみが開発されている。マスコット・キャラクターはGopher(ホリネズミ)。
発表当初はLinuxとMac 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]。2018年8月にリリースされたバージョン1.11からWebAssemblyをサポートした[13]。
名称
[編集]しばしばGolangやGo言語と表記されることがあるが、言語の正式名称はGoである[14]。golangという通称は、公式サイトのドメインであるgolang.orgに由来するものである。このドメインになったのはgo.orgが取得できなかったからであるとされている[14]。
また、公式のロゴではすべて大文字でGOと描かれているが、言語の名前としてはGoであるとされている[14]。
歴史
[編集]2009年11月10日にGoが初めて発表され[15]、バージョン1.0が2012年3月28日にリリースされた[16]。GoはGoogleの本番システムの一部で使用されており、他の多数の企業やオープンソースプロジェクトでも使用されている[17]。
主に次の2つの実装が存在する。
- GoogleのGoツールチェイン。複数のプラットフォームをサポートとしており、Linux、BSD、macOS、Plan 9、Windows、そして(2015年から)モバイルデバイスで動作する[18]。このGoコンパイラは、バージョン1.5からセルフホスティングされるようになった[19]。
- 2つ目のコンパイラgccgo。これは、GCCのフロントエンドである[20]。
Goの起源はGoogleのエンジニアであるRobert Griesemer、ロブ・パイク、ケン・トンプソンによる、新しいプログラミング言語を設計する実験的なプロジェクトに端を発する。Googleなど、多くのIT企業ではソースコードの複雑さや肥大化が問題になっていた。他の言語がよく受ける批判を解決するとともに、それらの言語のよい特徴をできる限り損なわないようにすることを目指した。彼ら設計者たちは、新しい言語として、以下の特徴を持つものを構想していた[21]。
- JavaやC++のように、静的に型付けされ、巨大なシステムでもスケールする
- RubyやPythonなどの動的な言語のように生産性が高く、リーダブルであり、過度なボイラープレートが必要ない[22]
- IDEが必須ではない。ただし、十分にサポートする
- ネットワークおよびマルチプロセッシングをサポートする
後のインタビューで、3人の言語設計者すべてが、新しい言語を設計する主なモチベーションとしてC++が好きでないことを共有していたことを述べている[23]。
バージョン1.6から、64ビットMIPS上で動作するLinux版および32ビットx86上で動作するAndroid版の実験的なポート(移植)が追加された[24][25]。
2017年7月13日、バージョン2への取り組みへの開始が発表された[26]。バージョン1.x系とバージョン2の開発は並行して行われ、バージョン1.20がバージョン2と同義となると宣言されている。
2018年4月、旧ロゴ(Gopherマスコット)がテキストと3本の直線からなる新しいデザインのものに変更された。しかし、マスコットはそのまま変わっていない。
2021年8月16日、バージョン1.17がリリースされる。64ビットのARMアーキテクチャー用のWindowsがサポートされる[27]。バージョン2系は、Nightly Build系であり、公式にはリリースされていない。これは、プログラム・アーキテクチャーの変更がユーザーに多大な負担を与える事に配慮しているためである(Rustからの反省)。
コード例
[編集]Hello world
[編集]次のコードはGoで書いたHello worldプログラムである。
package main
import "fmt"
func main() {
fmt.Println("Hello, World")
}
並行性の例
[編集]次のサンプルプログラムは、Goの並行性機能をデモンストレーションする非同期プログラムの実装である。2つの"goルーチン"(軽量スレッド)を立ち上げている。一方はユーザーがテキストを入力するのを待機し、他方はタイムアウトを実現する。select文が2つのgoルーチンがメインルーチンにメッセージを送信するのを待機し、最初に到達したメッセージに対して動作を実行する(コード例はDavid Chisnallの本のコードを一部修正している)[28]: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.")
}
}
変数の宣言と利用、型変換
[編集]次のコードはGoで書いた変数を宣言し実行するプログラムである。前記のコードにて、:=を利用した宣言が行われているが、その場合とは別の手段を使い表記する。testというstring型の変数とafterというstring型の変数とnumというint型の変数を宣言し、numstrという空のstring型の変数を使用し、numをstringに変換してnumstrに値を入れて、testとnumとafterを結合させてtextというstring型の変数に値を入れてコンソールに表示するものである。
package main
import (
"fmt"
"strconv"
)
func main() {
var test string = "Today is December "
var after string = " th"
var num int = 28
var numstr string
numstr = strconv.Itoa(num)
var text string = test + numstr + after
fmt.Println(text)
}
言語機能の特徴
[編集]文法
[編集]構文は様々な言語に部分的に類似している。変数などにおける型の記法はLimboから引き継いだものと思われる、型名を後置するもので、PascalやAdaに類似している。ブロックの区切りに波括弧を使う記法はC言語他多くと同様である。for文やif文では、条件式を丸括弧で括らず、帰結部分には波括弧が必須である。定数生成器iotaはASP由来である。他に、並列処理について、CSPを参考としている[29]、チャネルによるスレッド間通信機能の構文がある。
構造体
[編集]Goにはユーザー定義の複合型を記述することのできる構造体の機能があり、下記のように記述する。
package main
import "fmt"
// 新しい構造体型の定義。各フィールドの識別子とその型名を指定。
type Person struct {
name string // 名前。
age int // 年齢。
}
// メソッドの定義。
func (m *Person) PrintName() {
fmt.Printf("Name = %s\n", m.name)
//fmt.Printf("Name (%T) = %#v\n", m.name, m.name)
}
func (m *Person) PrintAge() {
fmt.Printf("Age = %d\n", m.age)
//fmt.Printf("Age (%T) = %#v\n", m.age, m.age)
}
func main() {
var person Person
person.name = "John Smith"
person.age = 24
// 以下のように書くこともできる。
//person := Person{ "John Smith", 24 }
person.PrintName()
person.PrintAge()
// 以下のように書式指定すると、型名とリテラル表現を出力できる。
fmt.Printf("person (%T) = %#v\n", person, person)
}
上記の例では、name
およびage
というフィールドを持つPerson
構造体の型を定義し、さらにその構造体に対応するPrintName()
およびPrintAge()
というメソッドを定義している。GoにはJavaのような言語が持つクラスやオブジェクトのような概念は無いが、実質的には構造体はそれらに近い役割を提供する。
Goでは、関数に(m *Person)
のようなレシーバーの宣言を追加してメソッドを定義する。レシーバーの宣言は (変数名 型名)
と記述する。レシーバー内の変数名は自由に指定が可能であり、受け取り方は値渡し、ポインター渡しの2種類から指定できる。
型とメソッドが切り離されており、型は一切メソッドに依存しない。メソッドと型は、メソッドの追加対象となる型名をレシーバーの型名に指定することで関連付けるようになっている。メソッドは型から切り離されているため型本体を変更する事なくメソッドを追加することが可能であり、同一パッケージ内限定ではあるが、開放された型を実現している。
Goには型自身を初期化したり終了したりする機能が存在しない。つまりJavaの静的イニシャライザやC#の静的コンストラクタに相当する機能がない。初期化は型に所属しない関数や、他の型のメソッドで行うようになっている。
新しい型の定義
[編集]Goではtype構文を利用して既存の型から新しい型を定義できる。
次の例では、int
型を元に MathInt
という新しい型を定義している。
type MathInt int
これはC/C++におけるtypedefやC#におけるusingエイリアスディレクティブと違い、単に既存の型に別名を与えているのではなく、元のint
型と別の型を定義している点が大きく異なる。
新しく定義した型であれば、元の型が構造体型でなくともメソッドを追加することができる。例えばint
型やマップ型といった事前定義済み型 (組み込み型) に対してもメソッドを追加することが可能である。
事前定義済み型から新しい型を定義した場合、例えば下記のような記述が可能となる。
package main
import (
"fmt"
"math"
)
type MathInt int
func (x MathInt) Abs() float64 {
return math.Abs(float64(x))
}
func main() {
var x MathInt = -10
fmt.Printf("%d\n", x) // -> -10
fmt.Printf("%f\n", x.Abs()) // -> 10.000000
fmt.Printf("%T\n", x) // -> main.MathInt
}
Go 1.9ではtypeキーワードを利用した型エイリアス (type alias) 構文が別途追加された[30]。こちらはC/C++のtypedefやC#のusingエイリアスディレクティブと類似の機能である。
package main
import (
"fmt"
"math"
)
type MyInt = int
func main() {
var x MyInt = -10
fmt.Printf("%d\n", x) // -> -10
fmt.Printf("%f\n", math.Abs(float64(x))) // -> 10.000000
fmt.Printf("%T\n", x) // -> int
}
匿名フィールド
[編集]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
文という例外安全な強制実行のしくみを採用している。
defer
文は、defer
キーワードに続けて、関数呼び出しまたはメソッド呼び出しを記述することで関数終了時に[注釈 1]指定した処理の呼び出しを実行する。
ファイルのオープンとクローズなど、組になる作業で両方行われる必要があるものに多く使われる。
defer
は下記の様に記述する。
defer 関数呼び出しまたはメソッド呼び出し
defer
はブロックを持たず入れ子が深くなるようなことはない。ただし、関数以外のブロックを無視する点に注意が必要である。例えばループ中でdefer
を使用した場合、ループ内では指定した関数が呼ばれず関数終了時にdefer
で指定した全ての関数呼び出しが行われる。
defer
には、関数呼び出しまたはメソッド呼び出ししか記述できず[注釈 2]、式や文を直接記述することはできないが、下記のように無名関数の即時実行を利用して、任意の式や文の実行も可能である。
defer func() {
message := "done"
fmt.Println(message)
}()
例外処理
[編集]他の言語で使われている方式の例外処理機能はない。Goにも例外があり、リカバーと合わせてtry-catch
のような事もできるが推奨されない。例外機能は、主に0除算など起きてはならないことに使われ普通は強制終了するが、リカバーによって強制終了しないようにする事も可能である。Goでは、事前に例外が起きないよう確認する事や、成功かどうかのBool値を返す事のような、例外を起こさない方法が好まれる。
ダウンキャスト
[編集]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を使うことで[31]、対象の値型やその値などの詳細を得ることができる。
名前とモジュールの管理
[編集]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の名前管理の特筆する点として名前解決の方法として階層型の名前空間ではなく別名の仕組(のみ)を採用している点が挙げられる。名前解決をするために長い名前[注釈 3]を記述する煩わしさはない。
更にGoにはモジュール管理の点でも特筆すべき点がある。Goはソースファイル中に一つ初期化関数(init
)を定義する事で、初期化関数により実行時に広域変数を初期化できるという機能を持っている。この初期化関数が呼ばれるソースファイルは、main
関数の定義されたソースファイルから連鎖的にimport
されたパッケージに含まれるソースファイルだけである。この特性により使う予定もないのに初期化コードが実行されるおそれはない。また、パッケージが初めて参照されるまで初期化が遅延されてしまうという事もない。
アクセス指定子
[編集]Go には、型レベルのアクセス指定子が存在しないが、パッケージレベルでのアクセス制御が存在する。パッケージレベルでのアクセス制限はシンボル名(変数名、メソッド名、関数など)の先頭一文字目が大文字か小文字かで決定する。大文字であればパッケージに対し公開され、小文字であれば非公開となる。
提供しない機能
[編集]型の継承、アサーション、オーバーロードといった機能をあえて提供していない。[29]FAQにおいてオーバーロードは効率的見地から排除されたことが述べられている。関数は多値を返すことができるので、それによりエラーの報告は容易である、としている。
処理系
[編集]Go言語の処理系は2種類ある[32]。
gc
はGo言語で実装されたセルフホスティングのGo言語コンパイラである。バージョン1.4以前はC言語で実装されていたが、バージョン1.5でC言語からGo言語への変換ツールでコンパイラの実装言語を切り替えて、以降はGo言語で開発している。構文解析にyacc・bisonを使用している。
Gccgo
は、再帰下降パーサを持つC++フロントエンド、バックエンドに標準GCCを利用したサードパーティー開発のGo言語コンパイラである。
脚注
[編集]注釈
[編集]出典
[編集]- ^ “Release History”. Template:Cite webの呼び出しエラー:引数 accessdate は必須です。
- ^ “Why doesn't Go have "implements" declarations?”. go.dev. 1 October 2015閲覧。
- ^ Pike, Rob (2014年12月22日). “Rob Pike on Twitter”. 2016年3月13日閲覧。 “Go has structural typing, not duck typing. Full interface satisfaction is checked and required.”
- ^ Kincaid, Jason (10 November 2009). “Google’s Go: A New Programming Language That’s Python Meets C++”. TechCrunch 18 January 2010閲覧。
- ^ “Language Design FAQ”. go.dev (16 January 2010). 27 February 2010閲覧。
- ^ Metz, Cade (5 May 2011). “Google Go boldly goes where no code has gone before”. The Register. 2018年7月24日閲覧。
- ^ go.dev
- ^ go installation guide
- ^ “Go version 1 is released” (2012年3月28日). 2012年3月29日閲覧。
- ^ “プログラミング言語「Go 1.4」がリリース、Androidを正式にサポート” (2014年12月12日). 2015年8月26日閲覧。
- ^ “Go 1.5 is released” (2015年8月19日). 2015年8月26日閲覧。
- ^ Google App Engine Blog, Tuesday, May 10, 2011
- ^ “Go 1.11 is released”. The Go Blog (24 August 2018). 2018年10月4日閲覧。
- ^ a b c Frequently Asked Questions (FAQ) - The Go Programming Language
- ^ “Hey! Ho! Let's Go!”. Google Open Source. Google. 17 May 2018閲覧。
- ^ Gerrand. “Go version 1 is released - The Go Blog”. 2018年2月4日閲覧。 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発行) 2017年8月6日閲覧. "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年7月24日閲覧。
- ^ “Go FAQ: Is Google using Go internally?” 2013年3月9日閲覧。
- ^ “Google's In-House Programming Language Now Runs on Phones”. wired.com (19 August 2015). 2018年7月24日閲覧。
- ^ “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年8月26日閲覧。
- ^ “FAQ: Implementation”. go.dev (16 January 2010). 2010年1月18日閲覧。 “Installing GCC: Configuration”. 2011年12月3日閲覧。 “Ada, Go and Objective-C++ are not default languages”
- ^ Pike, Rob (28 April 2010). “Another Go at Language Design”. Stanford EE Computer Systems Colloquium. Stanford University. 2018年7月24日閲覧。 Video available.
- ^ “Frequently Asked Questions (FAQ) - The Go Programming Language”. go.dev. 2016年2月26日閲覧。
- ^ Andrew Binstock (18 May 2011). “Dr. Dobb's: Interview with Ken Thompson”. 2014年2月7日閲覧。 Pike, Rob (2012年). “Less is exponentially more”. 2018年7月24日閲覧。 Robert Griesemer (2015年). “The Evolution of Go”. 2018年7月24日閲覧。
- ^ Go 1.6 Release Notes - The Go Programming Language
- ^ 末岡洋子 (2016年2月19日). “「Go 1.6」リリース、細かな性能改善などがメインのリリースに”. 2016年2月20日閲覧。
- ^ Cox, Russ. “Toward Go 2 - The Go Blog”. 2018年2月4日閲覧。
- ^ “Go 1.17 is released - go.dev”. go.dev. 2021年9月19日閲覧。
- ^ Chisnall, David (2012). The Go Programming Language Phrasebook. Addison-Wesley
- ^ a b https://go.dev/doc/faq
- ^ Go 1.9 Release Notes - The Go Programming Language
- ^ reflect - The Go Programming Language
- ^ §Implementation | Frequently Asked Questions (FAQ) - The Go Programming Language
関連項目
[編集]解説書籍など
[編集]- 松木雅幸、mattn、藤原俊一郎、中島大一、上田拓也、牧大輔、鈴木健太:「改訂2版 みんなのGo言語」、技術評論社、ISBN 978-4-297-10727-7(2019年8月1日)。
- Alan A.A.Donovan, Brian W.Kernighan 著、 柴田芳樹 翻訳:「プログラミング言語Go」、丸善出版、ISBN 978-4-621-30025-1(2016年6月20日)。
Goのtourを直接ダウンロードして、オフラインで学習することも可能である。方法としては、まずオフィシャルサイトから最新版のgo-langをダウンロードしてインストールした後、go install golang.org/x/website/tour@latest
を実行することで必要最小限のファイルがダウンロードされる。その後、tourを実行することで、オフラインでGoの文法を学ぶことができる。