PR

Goの基本的な文法を学ぼう

2016年11月29日(火)
松尾 将幸
2つの簡単なプログラムの作成を通じて、Go言語の特徴的な文法を学んでいく。

はじめに

前回は、Go言語の概要紹介とHello, Worldの実行までを行いました。今回はさらにGoの魅力を理解してもらえるように、簡単なプログラムを2つ作成し、それを通して型やインターフェースといったGoの文法を紹介していきたいと思います。なお、以下ではLinuxもしくはMacで作業することを前提として書いていますが、WindowsでもコマンドプロンプトやPowerShellで同じようにできますので、ご安心ください。

Webサーバを作ってみよう!

まずはGoの威力を知るため、簡単なWebサーバを作ってみましょう。「server.go」というテキストファイルを任意の場所に作成し、以下のようにエディタで書いてみてください。

リスト1:Goで作る簡単なWebサーバ

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」コマンドを使ってバイナリを作ってみましょう。

リスト2:go buildコマンドの実行例

$ go build -o server server.go

「go build」コマンドの実行後に、何も表示されなければ実行は成功です。これで、「go build」コマンドを実行した環境に最適化されたバイナリファイルが生成されます。なお、何らかのエラーメッセージが表示された場合には表示されるエラーメッセージと、エラーが発生したファイル名と行数を見てエラーを修正し、再度「go build」を実行してみましょう。

リスト3:関数名を間違えた場合のエラーメッセージ例

$ go build -o server server.go
# command-line-arguments
./server.go:15: undefined: ServeHTTP

成功していれば、同じディレクトリに「server」という名前のバイナリファイルが作成されているはずです。早速、それをターミナル上で実行してみます。

リスト4:作成したWebサーバを実行

$ ./server
Web Server Start!

ここでブラウザから「http://localhost:8080」にアクセスしてみましょう。以下のように表示されていれば成功です。

Goで作成したWebサーバが起動している

Goで作成したWebサーバが起動している

このようにGoを使えば、わずか20行にも満たない行数でWebサーバを作って動かすことができます。なお「server」を停止する場合は、ターミナル上で「Ctrl」キーと「C」を同時に押して停止してください。

プログラムの内容を理解しよう!

それでは今作成したプログラムを解説していきます。

パッケージ

リスト5:package

package main

これは「server.go」ファイルが所属する「パッケージ」を宣言しています。Goでは、全てのプログラムは何かしらのパッケージに所属している必要があります。また、Goのプログラムが起動して一番最初に実行される処理は「main」パッケージに属する「main()関数」となります。もしmainパッケージがなかったり、「main()関数」がなかったりしたらどうなるのでしょうか?

リスト6:例:mainパッケージがない場合

$ go run server.go
go run: cannot run non-main package

リスト7:例:main関数がない場合

$ 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関数がなければ、実行時にエラーとなってしまいます。そのため、必ず書くようにしましょう。

インポート

リスト8:import

import (
    "fmt"
    "net/http"
)

この部分では「server.go」が使用する他のパッケージを宣言しています。mainパッケージの中に書かれていない関数を使う場合、その関数を持つパッケージ名を記載しなければなりません。今回は「fmtパッケージ」と「net/httpパッケージ」に含まれている関数を使っているため、この2つのパッケージを宣言する必要があります。

関数

リスト9:関数

func ServeHTTP(w http.ResponseWriter, r *http.Request) {
    var text string
    text = "Hello, World"
    w.Write([]byte(text))
}

ここでは「ServeHTTP関数」を定義し、その処理内容を記述しています。関数は基本的に以下の書式で書き表します。角カッコの部分は、必要ない場合には省略可能です。

リスト10:関数の書式

func 関数名 ([引数1, 引数2,…]) [返り値の型1, 返り値の型2,…] {
    // 処理内容
    // 返り値がある場合、「return 返り値1, 返り値2,…」を記述すること
}

関数を使う場合、同じパッケージであれば「関数名([引数])」のように記述し、他のパッケージの場合は、「パッケージ名.関数名([引数])」と書きます。

また、返り値は複数返すことが可能です。Goで頻出するイディオムとして、以下のように1つ目の返り値が関数の結果、2つ目の返り値はエラーが発生したかどうかを表す、というコードがよく登場します。

リスト11:よく使われる返り値の書き方

result, err := something()

さらに返り値を受ける時、「 _(アンダースコア)」を使用すると、変数を定義せずに返り値を無視することが可能です。以下のように書けば、関数の中でエラーが発生したかどうかだけを受け取れます。

リスト12:エラー発生の有無だけを受け取る書き方

_, err := something()

変数と型

Goでは、変数を以下のように宣言します。

リスト13:変数宣言の書式

var 変数名 型名

「ServeHTTP関数」では、以下のように1行目で「string型」の変数「text」を宣言し、2行目で文字列を代入しています。

リスト14:ServeHTTP関数での変数宣言の例

var text string
text = "Hello, World"

Goには変数の型として、以下のような多種多様な型が用意されています。

値型変数の一覧

種別型名説明
整数型int32bitもしくは64bitの整数を表す
int88bitの整数を表す
int1616bitの整数を表す
int3232bitの整数を表す
int6464bitの整数を表す
uint32bitもしくは64bitの符号なし整数を表す
uint8(別名byte)8bitの符号なし整数を表す
uint1616bitの符号なし整数を表す
uint3232bitの符号なし整数を表す
uint6464bitの符号なし整数を表す
浮動小数点数型float3232bitの浮動小数点数(小数)を表す
float6464bitの浮動小数点数(小数)を表す
複素数型complex6464bitの複素数を表す(実部32bit、虚数部32bit)
complex128128bitの複素数を表す(実部64bit、虚数部64bit)
文字型string文字列を表す
runeUnicodeの文字コードを表す
真偽値booltrueもしくはfalseを表す

参照型変数の一覧

種別型名説明
スライス[]型名要素数不定の配列を表す
マップmap[型名]型名2つの任意の型(関数型と参照型を除く)を紐付けるkey/value形式を表す
チャネルchan 型名複数のゴルーチンの間でデータを受け渡しするときに使用する

特殊型変数の一覧

種別型名説明
構造体type struct複数のデータを1つのまとまりとして表す
関数型func関数を表す。関数を変数に代入できる
インターフェース型interface{}「任意の型」を表し、どのような値でも代入可能。ただし計算には使えない
ポインタ型&(型名)変数のポインタを表す

Goは型システムは非常に厳密であり、異なる型の変数同士の直接代入はできず、他の言語のような暗黙の型変換もありません。そのため、以下のような代入はエラーになります。

リスト15:Goでエラーとなる代入の例

var a int
var b float
b = 10.0
a = b  // エラー

また整数型の「int」は、インストールしたGoのbit数によって32bitか64bitのどちらかになります(64bit版OSに32bit版のGoをインストールすれば、int32と同じになります)。ただしどちらの場合であっても、「int32」や「int64」との互換性はありません。よって、以下の代入は失敗します。

リスト16:Goでエラーとなる代入の例intとint64

var a int   // 64bit版のGoとする
var b int64
b = 100
a = b  // エラー

もし異なる型の変数に値を入れたいのであれば、「キャスト(明示的な型変換)」が必要となります。

リスト17:キャスト

var a int
var b float
b = 10.0
a = int(b)  // 成功、aに10が入る

このようにGoは厳密な型システムを持つことで、ビルド時にプログラムのミスをより多く見つけることが可能になります。さらにGoの素晴らしいところは、型の恩恵を享受しつつプログラムの記述量を減らせる「型推論」の仕組みを持つところです。

リスト18:変数宣言の例

var text string
text = "Hello, World"

例えば上の2行の例は、以下のように1行で書くことができます。

リスト19:型推論を利用して記述方法

text := "Hello, World"

ここでは「var」も「string」もありませんが、「text」という新しい変数を宣言したうえで、それを「Hello, World」という文字列で初期化しています。まず「=」が「:=」となっている点に注目してください。これは「新しく変数を定義する」という意味になります。その上で、Goのコンパイラは、「『Hello, World』という文字列を入れているので、このtextという変数はstring型だ」と判断し、プログラマが何も書かなくてもtext変数をstring型として扱ってくれます。よって、以下のコードはエラーと判定されます。

リスト20:型推論と異なる型への代入はエラー

text := "Hello, World"
text = 100  // エラー

この言語仕様のため、Goを使うプログラマが型に悩まされることはあまり多くありません。さらにGoのコンパイラは、未使用の変数があった場合もエラーとしてくれるなど、コードの可読性に徹底的に気を配った仕様となっています。

配列

値型の変数に関しては、配列とすることができます。配列の書式は、以下のようになります。

リスト21:配列の書式

var 変数名 [要素数]型名 // 宣言のみ:この[]は省略可能という意味ではありません
変数名 := [要素数]型名{値1, 値2,…} // 初期化まで行う場合

例えばint型配列の初期化を行う場合は、以下のように書きます。

リスト22:int型配列の初期化

numbers := [3]int{1,2,3}

なお要素数も「型」の一部であることに注意してください。そのため、「[3]int」と「[5]int」は全く別の型として扱われます。

条件分岐

Goではif文は以下のように書きます。

リスト23:if文の書式

if 分岐条件 {
    // 処理内容
} else if 分岐条件 {
    // 処理内容
} else {
    // 処理内容
}

分岐条件を丸括弧で囲わないところが特徴的ですが、それ以外は他の言語と特別変わるところはありません。例えば同値判定をする場合は、以下のような書き方をします。

リスト24:同値判定の書き方

if i == 0 {
    fmt.Println("処理成功")
}

main関数

前述のとおり、Goのプログラムには必ず「main関数」が必要です。「server.go」のmain関数は、以下のようになっています。

リスト25: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が簡単に高度なプログラムを書ける言語だということを理解してもらえたのではないでしょうか。

株式会社インテリジェンス

1984年生まれ、大阪出身。SIerにて衛星搭載系システムやスパコンのWebポータル開発に10年ほど関わった後、2016年7月より現職。
ディレクター兼サーバサイドエンジニアとして、新規Webサービスの開発に従事。

連載記事一覧

開発言語技術解説

Goの基本的な文法を学ぼう

2016/11/29
2つの簡単なプログラムの作成を通じて、Go言語の特徴的な文法を学んでいく。
開発言語技術解説

Go言語はじめの一歩

2016/11/17
さまざまな分野で事例が増えつつあるGo言語、まずは簡単なサンプルブログラムを書いてみる。

Think IT会員サービスのご案内

Think ITでは、より付加価値の高いコンテンツを会員サービスとして提供しています。会員登録を済ませてThink ITのWebサイトにログインすることでさまざまな限定特典を入手できるようになります。

Think IT会員サービスのご案内

関連記事