Goのエキスパートが教えるGoの魅力、プログラミング基礎入門

上田拓也(@tenntenn)と申します。私はGoogle Developer ExpertでGoを担当しており、Go Conferenceの主催やgolang.tokyoなど各種コミュニティ活動も行っています。また、Goを採用している各社の技術顧問として開発現場にも携わっています。

今回はGoの基礎をご紹介します。私が公開している「Goで家計簿アプリを作ろう」というハンズオンの内容も一部抜粋して解説しているので、Goをはじめたばかりの初心者の方はぜひ参考にしていただけると嬉しいです。

Goの概要

サーバーサイドで使われる言語

最初に、Goの概要をご説明します。Goは2009年11月にGoogleがオープンソースで公開したプログラミング言語です。2020年2月に最新バージョン1.14がリリースされました。バージョンアップは大体半年ごとのペースです。 Goを利用できる領域はサーバーサイドがメインで、Google App Engine for GoというPaaSを使うことができます。これまで著名なOSSがGoで開発されており、代表的なのがDockerやKubernetes、gVisorなどです。

Goを学ぶ理由

Goはこれまで多くの企業のプロダクトで採用されていますが、静的型付け言語である点、文法がシンプルな点がチーム開発に向いているからです。パフォーマンスの良いコードを書きやすいのもメリットです。スタートアップ段階の企業から大手企業の大規模開発まで幅広く対応できる点も、Goが採用されている理由の一つでしょう。

Goの特徴

強力でシンプルな言語設計と文法

Goは非常にコードが書きやすく、冗長な記述は不要です。しかし、暗黙の型変換など曖昧な記述はできなくなっています。Goはシンプルさを大切にしている言語なため、どんどん機能を増やすことで言語を拡張するということありません。そのため、他の言語に慣れ親しんだ方がGoに触れるとシンプルさに驚くかもしれません。

並行プログラミング

これがGoの一番有名な特徴かもしれません。ゴールーチンと呼ばれる軽量なスレッドに近いものがあり、並行プログラミングが簡単に行えます。具体的には、関数呼び出しの前にgoキーワードをつけると、新しいゴールーチンを作り、そこで処理が行われます。

// 関数fを新しいゴールーチンで呼び出す
go f()

並行プログラミングでは、ゴールーチン間のデータのやりとりが難しい点の一つです。しかし、Goの場合はチャネルというデータ形式があり、それを用いて安全にデータをやりとりできるようになっています。

豊富な標準ライブラリ群

Goは、標準ライブラリが充実しています。標準ライブラリだけを用いてHTTPサーバを作れたり、JSONやXML、CSVなどの形式を扱う事ができます。

fmt 書式に関する処理など
net/http HTTPサーバなど
archive, compress 圧縮形式など
encoding JSON, XML, CSVなど
html/template HTMLテンプレート
os, path/filepath ファイル操作など

周辺ツールの充実

標準ライブラリと同様、周辺ツールも充実しています。例えば、gofmtというコードフォーマッターが標準で用意されてる事は有名でしょう。gofmtによってコードの書式が一意に決まるため、プロジェクトごと細かなコーディング規約などを設ける必要がありません。

go build ビルドを行うコマンド
go test xxxx_test.goに書かれたテストコードの実行
go doc, godoc ドキュメント生成
gofmt, goimports コードフォーマッター
go vet, golint コードチェッカー、リンター
gopls Language Server Protocol (LSP) の実装

シングルバイナリ・クロスコンパイル

最後に特徴として挙げたいのは、シングルバイナリとクロスコンパイルです。Goはコードをコンパイルするとシングルバイナリになります。そのため、生成されたバイナリだけあれば実行環境で処理系など用意しなくても実行が可能です。また、クロスコンパイルもできます。例えば、macOSでWindows向けやLinux向けのバイナリをクロスコンパイルするには以下のようにGOOSとGOARCHの2つの環境変数を設定します。

# Windows(32ビット)向けにコンパイル
$ GOOS=windows GOARCH=386 go build

# Linux(64ビット)向けにコンパイル
$ GOOS=linux GOARCH=amd64 go build

※go buildはGoのソースコードをコンパイルするコマンド

Goを触って家計簿アプリを作ってみよう

言語を学ぶにはハンズオンで触ってみるのが一番なので、GitHubにある家計簿アプリの作り方でご紹介している内容を一部抜粋してご説明します。 全部で11のステップがあり、skeletonディレクトリにあるTODOコメントの部分を問いていく形式になっています。答えがわからないときはsolutionディレクトリにある解答を参照できます。

例1:データの入力(STEP02)

STEP02では変数・型や標準パッケージの使い方、fmtパッケージを使った表示と入力について学びます。

<変数> Goは静的型付け言語なので変数には型があり、実行前に型の不一致を検出できます。この点はGoの良いところです。

変数定義は全てnに100を入れるという定義で、書き方の基本はこちらになっています。

// 変数定義と代入が一緒
var n int = 100

2行に分けると以下になります。

// 変数定義と代入が別
var n int
n = 100

右辺の値から左辺の型が推論できるので、型を省略もできます。

// 型を省略(int型になる)
var n = 100

// varを省略
// 関数内のみでしかできない
n := 100

まとめる場合は以下です。

// まとめる
var (
n = 100
m = 200
)

最初から使える組込み型は以下のようになります。

整数 int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, byte, rune
浮動小数点数 float32, float64
複素数 complex64, complex128
文字列 string
真偽値 bool
エラー error

Goの変数は「ゼロ値」とよばれる初期値が必ずあるため、どの変数も定義するだけで使えます。

ゼロ値
intやfloat64などの数値 0
string “”
Bool false
error nil

<標準パッケージ> Goのプログラム構成要素はパッケージ、型、関数、変数、定数で、パッケージ同士を組み合わせることでプログラムを作ります。パッケージの種類はmain、標準、サードパーティの3種類です。標準パッケージはGoが最初から用意しているパッケージで、100種類以上存在しています。

例えば以下ではfmtパッケージをインポートしていて、Println関数を呼び出しています。

package main

import "fmt"

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

下記ではfmt.Print関数とfmt.Println関数の説明をしています。複数の値を渡せるので、デバッグに使うと便利です。複数の値を渡すとスペース区切りで表示されます。

package main

import "fmt"

func main() {
fmt.Print("Hello, ") // 改行されない
fmt.Println("世界") // 改行される
fmt.Println("A", 100, true, 1.5) // スペース区切りで表示される
}

fmt.Printf関数はCのprintf関数と似ていて、書式を指定して標準出力します。Cに無い書式もあるため、一度ドキュメントを読むことをおすすめします。

package main

import "fmt"

func main() {
fmt.Printf("Hello, 世界\n") // \nで改行する
fmt.Printf("%d-%s", 100, "偶数") // %dは整数、%sは文字列
}

引数で指定した変数がある場所(ポインタ)に入力したデータを入れるfmt.Scan関数もあります。下記で家計簿で実際に打ちこむ数値の入力が行えます。

package main
import "fmt"
func main() {
var price int
fmt.Print("値段>")
fmt.Scan(&price)
fmt.Printf("%d円\n", price)
}

STEP02までの内容で、コマンドラインから計簿における値段と品目を入れて記録する部分ができるようになります。

例2:データの記録(STEP03)

STEP03では制御構文や構造体とユーザー定義型、関数を学びます。

<制御構文> 例えばif文ですね。ここはほかの言語とさして変わりはありません。

if x == 1 {
println("xは1")
} else if x == 2 {
println("xは2")
} else {
println("xは1でも2でもない")
}

GoのifがCやJavaと違うのは()が不要な点です。必ず{}で囲む必要があります。{}を改行はできません。

// ()がいらない
if a == 0 {
}
// できない
if a == 0
{
}
// できない
if (a == 0) fmt.Println(a)

条件分岐の前にセミコロンで区切って、代入文を書くことができます。ここで定義した変数はifとelseのブロックで使えます。

// 代入文を書く
if a := f(); a > 0 {
fmt.Println(a)
} else {
fmt.Println(2*a)
}

switchには若干Goならではの特徴があります。まずcaseにカンマ区切りでcase 1,2:のように書くことができます。また、breakも省略できます。caseをまたぐ際はfallthroughを使います。

switch a {
case 1, 2:
fmt.Println("a is 1 or 2")
default:
fmt.Println("default")
}

あとはCとは違いcaseに式を使えます。大量のif-elseをつなぐよりは見通しが良くなるでしょう。

switch {
case a == 1:
fmt.Println("a is 1")
}

Goには繰り返しの構文がforしかありません。

// 初期値;継続条件;更新文
for i := 0; i <= 100; i = i + 1 {
}
// 継続条件のみ
for i <= 100 {
}
// 無限ループ
for {
}
// rangeを使った繰り返し
for i, v := range []int{1, 2, 3} {
}

無限ループから抜け出すためのキーワードとしてbreakを用います。

// breakによる無限ループの脱出
func main() {
var i int
for {
if i%2 == 1 {
break
}
i++
}
}

switchの中でbreakするとswitchがbreakされるので、ラベルをつけてbreakするようにしましょう。

// ラベル指定のbreak
func main() {
var i int
LOOP:
for {
switch {
case i%2 == 1:
break LOOP
}
i++
}
}

本記事では、データの入力(STEP02)、データの記録(STEP03)のみ抜粋しましたが、是非、GitHubにある 家計簿アプリの作り方に挑戦してみてください。

Goのおすすめ勉強法

勉強法はいくつかありますが、まずはブラウザ上での公式のチュートリアルを利用することをおすすめします。あとはGoチームが運営しているgo.devで学習教材や事例が紹介されていますね。公式ドキュメントを読んでみるのもいいでしょう。書籍で学ぶ場合は以下がおすすめです。

『プログラミング言語Go』(丸善出版)Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)

『みんなのGo言語』(技術評論社)松木雅幸、mattn、藤原俊一郎、中島大一、上田拓也、牧大輔、鈴木健太

『Goならわかるシステムプログラミング』(ラムダノート)渋川よしき

『Go言語による並行処理』(オライリージャパン)Katherine Cox-Buday (著), 山口 能迪 (翻訳)

日本には全国にGoのコミュニティがあるので、そちらも利用してみるといいでしょう。女性や学生限定のコミュニティもあります。GopherConなど海外のGoのカンファレンスなんかも、YouTubeで動画が公開されていますから、上手く利用してみてくださいね。

 

上田拓也 Goのエキスパート、上田拓也さん 上田さんが技術顧問として参画されているシェアフル株式会社のCIO・松尾さんと技術顧問3名の対談記事はこちらをご覧ください。

FLEXYとはABOUT FLEXY

『FLEXY』はエンジニア・デザイナー・CTO・技術顧問を中心に
週2-3日 x 自社プロダクト案件を紹介するサービスです