Goの基本的な文法を学ぼう
(編注:2017年7月21日15時20分更新)記事公開当初の内容に誤植と技術的な誤りがありました、お詫びして訂正致します(解説を大幅に追加しました)。今回の修正と第3回以降の記事では外部監修者によるチェック体制を強化しております。
はじめに
前回は、Go言語の概要紹介とHello, Worldの実行までを行いました。今回はさらにGoの魅力を理解してもらえるように、簡単なプログラムを2つ作成し、それを通して型やインターフェースといったGoの文法を紹介していきたいと思います。なお、以下ではLinuxもしくはMacで作業することを前提として書いていますが、WindowsでもコマンドプロンプトやPowerShellで同じようにできますので、ご安心ください。
Webサーバを作ってみよう!
まずはGoの威力を知るため、簡単なWebサーバを作ってみましょう。「server.go」というテキストファイルを任意の場所に作成し、以下のようにエディタで書いてみてください。
package main import ( "fmt" "net/http" ) func ServeHTTP(w http.ResponseWriter, r *http.Request) { var text string text = "Hello, World" w.Write([]byte(text)) } func main() { http.HandleFunc("/", ServeHTTP) fmt.Println("Web Server Start!") err := http.ListenAndServe("localhost:8080", nil) if err != nil { fmt.Println("Error!") } }
前回は「go run」コマンドを使ってプログラムを実行しましたが、今回は「go build」コマンドを使ってバイナリを作ってみましょう。
$ go build -o server server.go
「go build」コマンドの実行後に、何も表示されなければ実行は成功です。これで、「go build」コマンドを実行した環境に最適化されたバイナリファイルが生成されます。なお、失敗した場合、例えば以下のようにエラーメッセージが表示されます。エラーが発生したファイル名と行数、エラーメッセージが表示されますので、それを見てエラーを修正し、再度「go build」を実行してみましょう。
$ go build -o server server.go # command-line-arguments ./server.go:15: undefined: ServeHTTP
成功していれば、同じディレクトリに「server」という名前のバイナリファイルが作成されているはずです。早速、それをターミナル上で実行してみます。
$ ./server Web Server Start!
ここでブラウザから「http://localhost:8080」にアクセスしてみましょう。以下のように表示されていれば成功です。
このようにGoを使えば、わずか20行にも満たない行数でWebサーバを作って動かすことができます。なお、「server」を停止する場合は、ターミナル上で「Ctrl」キーと「C」を同時に押して停止してください。
プログラムの内容を理解しよう!
それでは今作成したプログラムを解説していきます。
3.1 パッケージ
package main
これは「server.go」ファイルが所属する「パッケージ」を宣言しています。Goでは、全てのプログラムは何かしらの「パッケージ」に所属している必要があります。また、Goのプログラムが起動した直後に「main」パッケージに属する「main関数」が実行されます(実際には、それより先に各パッケージの「init関数」が実行されます)。もし「mainパッケージ」がなかったり、「main関数」がなかったりしたらどうなるのでしょうか?
$ go run server.go go run: cannot run non-main package
$ go run server.go # command-line-arguments runtime.main: call to external function main.main runtime.main: main.main: not defined runtime.main: undefined: main.main
このように、「mainパッケージ」や「main関数」がなければ、実行時にエラーとなってしまいます。そのため、必ず書くようにしましょう。
インポート
import ( "fmt" "net/http" )
この部分では「server.go」が使用する他のパッケージを宣言しています。「mainパッケージ」の中に書かれていない関数を使う場合、その関数を持つパッケージ名を記載しなければなりません。今回は「fmtパッケージ」と「net/httpパッケージ」に含まれている関数を使っているため、この2つのパッケージを宣言する必要があります。
関数
func ServeHTTP(w http.ResponseWriter, r *http.Request) { var text string text = "Hello, World" w.Write([]byte(text)) }
ここでは「ServeHTTP関数」を定義し、その処理内容を記述しています。関数は基本的に以下の書式で書き表します。角カッコの部分は、必要ない場合は省略可能です。
func 関数名 ([引数1, 引数2,…]) [返り値の型] { // 処理内容 // 返り値がある場合、「return 返り値1, 返り値2,…」を記述すること。 }
関数を使う場合、同じパッケージであれば「関数名([引数])」のように記述し、他のパッケージの場合は、「パッケージ名.関数名([引数])」と書きます。
また、返り値は複数返すことが可能です。複数返す場合は、「(返り値の型1, 返り値の型2,…)」のように丸括弧で囲む必要があります。 さらに、返り値は、引数と同じように識別子をつけることができます。その場合、以下のような書き方が可能となります。
func GetNumber() (n int) { n = 10 return // return n や return 10と書いても問題ありません。 }
int型の返り値にあらかじめ「n」という識別子を付けておき、関数内で10を代入してreturnしています。この時「return n」と書く必要はありません。単にreturnと書くだけでnを返してくれます。
なお、Goで頻出するイディオムとして、以下のように1つ目の返り値が関数の結果、2つ目の返り値はエラーが発生したかどうかを表す、というコードがよく登場します。
result, err := something()
さらに返り値を受ける時、「 _(アンダースコア)」を使用すると、変数を定義せずに返り値を無視することが可能です。以下のように書けば、関数の中でエラーが発生したかどうかだけを受け取れます。
_, err := something()
変数と型
Goでは、変数を以下のように宣言します。
var 変数名 型名
「ServeHTTP関数」では以下のように1行目で「string型」の変数「text」を宣言し、2行目で文字列を代入しています。
var text string text = "Hello, World"
Goには、変数の型として、以下のような多種多様な型が用意されています。
種別 | 型名 | 説明 |
---|---|---|
整数型 | int | 32bit、もしくは64bitの整数を表す |
int8 | 8bitの整数を表す | |
int16 | 16bitの整数を表す | |
int32 | 32bitの整数を表す | |
int64 | 64bitの整数を表す | |
uint | 32bit、もしくは64bitの符号なし整数を表す | |
uint8(別名byte) | 8bitの符号なし整数を表す | |
uint16 | 16bitの符号なし整数を表す | |
uint32 | 32bitの符号なし整数を表す | |
uint64 | 64bitの符号なし整数を表す | |
浮動小数点数型 | float32 | 32bitの浮動小数点数(小数)を表す |
float64 | 64bitの浮動小数点数(小数)を表す | |
複素数型 | complex64 | 64bitの複素数を表す(実部32bit、虚数部32bit) |
complex128 | 128bitの複素数を表す(実部64bit、虚数部64bit) | |
文字型 | string | 文字列を表す |
rune | int32と同型。Unicodeのコードポイントを表すために使用 | |
真偽値 | bool | true、もしくはfalseを表す |
配列 | [要素数]型名 | 要素数で指定した数の要素を持つリストを実現する。 |
スライス | []型名 | 可変長の要素を持つリストを実現する |
マップ | map[型名]型名 | 2つの任意の型(関数型とスライス、マップ、チャネルを除く)を紐付けるkey/value形式を表す |
チャネル | chan 型名 | 複数のゴルーチンの間でデータを受け渡しするときに使用する |
関数型 | func | 関数を表す。関数を変数に代入できる |
構造体 | struct{ フィールドの一覧 } | 複数のデータを1つのまとまりとして表す |
インターフェース | interface{ メソッドの一覧 } | 実装すべきメソッドを定義した型(詳細は後述)。 |
ポインタ | *(型名) | 値が格納されているメモリへのアドレスを表す |
Goは型システムは非常に厳密であり、異なる型の変数同士の直接代入はできず、他の言語のように「暗黙の型変換」もありません。そのため、以下のような代入はエラーになります。
var a int var b float b = 10.0 a = b // エラー
また整数型の「int」は、インストールしたGoのbit数によって「32bit」か「64bit」のどちらかになります(32bit版のGoをインストールすれば「int32」と同じになります)。ただし、どちらであっても、「int32」や「int64」との互換性はありません。よって、以下の代入は失敗します。
var a int // 64bit版のGoとする var b int64 b = 100 a = b // エラー
もし異なる型の変数に値を入れたいのであれば、「キャスト(明示的な型変換)」が必要となります。
var a int var b float b = 10.0 a = int(b) // 成功、aに10が入る
このように厳密な型システムを持つことで、ビルド時にプログラムのミスをより多く見つけることが可能になります。さらにGoの素晴らしいところは、型の恩恵を享受しつつプログラムの記述量を減らせる「型推論」の仕組みを持つところです。
var text string text = "Hello, World"
例えば上の2行の例は、以下のように1行で書くことができます。
text := "Hello, World"
text := "Hello, World" text = 100 // エラー
この言語仕様のため、Goを使うプログラマが型に悩まされることはあまり多くありません。さらにGoのコンパイラは、未使用の変数があった場合もエラーとしてくれるなど、コードの可読性に徹底的に気を配った仕様となっています。
配列
値型の変数に関しては、配列とすることができます。配列の書式は以下のようになります。
var 変数名 [要素数]型名 // 宣言のみ:この[]は省略可能という意味ではありません 変数名 := [要素数]型名{値1,値2,…} // 初期化まで行う場合
また、例えばint型配列の初期化を行う場合は,以下のように書きます。
numbers := [3]int{1,2,3}
要素数も「型」の一部であることに注意してください。そのため、「[3]int」と「[5]int」は全く別の型として扱われます。
なお、Goにおいては、配列よりもスライスがよく使われます。スライスであれば、配列のように要素数が固定されないためです。
var 変数名 []型名
条件分岐
Goではif文は以下のように書きます。
if 分岐条件 { // 処理内容 } else if 分岐条件 { // 処理内容 } else { // 処理内容 }
分岐条件を丸括弧で囲わないところが特徴的です。また、以下のように分岐条件の前に代入文を分けて書くこともできます。
if err := foo() ; err != nil { // エラー処理 }
変数への代入と、それを使った判定を一度に書けるのは他の言語でもできますが、文の終端を意味するセミコロンを間に挟んで、2つの式をまとめて書くことができるというのはGoならではでしょう。また、例えば同値判定をする場合は以下のような書き方をします。
if i == 0 { fmt.Println("処理成功") }
main関数
前述のとおり、Goのプログラムには必ず「main関数」が必要です。「server.go」の「main関数」は以下のようになっています。
func main() { http.HandleFunc("/", ServeHTTP) fmt.Println("Web Server Start!") err := http.ListenAndServe("localhost:8080", nil) if err != nil { fmt.Println("Error!") } }
最初の「http.HandleFunc」は、「httpパッケージ」に属する「HandleFunc」を呼び出しています。「HandleFunc」の引数として、「"/"」と「ServeHTTP(関数)」を渡しています。
これはブラウザから「Webサーバのドキュメントルート」にアクセスされた際に、自動的に「ServeHTTP」が呼び出されるように登録をしています。その次の「fmt.Println」は、バイナリファイルを実行した時に、コマンドラインに「Web Server Start!」と表示するためのものです。
最後の「http.ListenAndServe」は「ホスト名」「localhost」、「ポート番号」「8080」でブラウザからのアクセスを待ち受けるWebサーバを起動することを表しています。
Webサーバとして起動するために必要なコードは、1行目と3行目の2行だけです。Goが簡単に高度なプログラムを書ける言語だということを理解してもらえたのではないでしょうか。