Go
golang
新人研修
新人教育
新人プログラマ応援
More than 1 year has passed since last update.


What's this?

普段携わっている、 「圧倒的にいちばん速く覚えられる 英単語アプリmikan」 を作っているスタートアップ

でGo言語の社内勉強会を開きましたので資料を共有します。

iOS版 App Storeページ

Android版 Google Playページ

スクリーンショット 2017-03-31 22.20.01.png



What's Go?


  • 2009年11月にGoogleが公開



  • 特徴


    • オープンソース

    • シンプル

    • コンパイルが早い

    • 並列処理をサポート

    • ガベージコレクションによるメモリ管理

    • 安全性が高い

    • 継承がない

    • 容易なエラーハンドリング

    • C言語Like



  • HP

    https://golang.org/

    (見たほうが良い!)




Goを利用したOSS

コードとか読んでみると勉強になるかも



Install Go

$ brew install go

.zshrcもしくは.bashrc等に以下を追加してsource ~/.zshrcコマンドで設定を適用。

# Go

export GOPATH=${HOME}/.golang
export PATH=${PATH}:${GOROOT}/bin:${GOPATH}/bin



インストールできたか確認

$ go version

go version go1.8 darwin/amd64



Hello, World!

基本的なテンプレートは以下のようになります。


hello.go

package main

import "fmt"

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




実行

$ go run hello.go

または

$ go build hello.go

$ ./hello



入門



  • 変数・定数

  • 演算子

  • for

  • if, switch

  • 関数


    • 定義方法

    • 可変長パラメータ

    • 名前付き戻り値

    • 関数リテラル



  • 遅延実行

  • 構造体、インタフェース

  • 配列

  • スライス

  • マップ

  • エラーハンドリング

  • エラー処理


    • パニック、リカバリ



  • Goルーチン


    • チャネル: 作成、クローズ






  • 論理値型: bool

  • 数値型: int,float64

  • 文字列型: string



(+α) 型


  • 論理値型


    • bool




  • 数値型


    • uint8、uint16、uint32、uint64

    • int8、int16、int32、int64

    • float32、float64

    • complex64、complex128

    • byte

    • rune

    • uint

    • int

    • uintptr




  • 文字列型


    • string





変数

var a string

a = "hoge"

var b string = "hoge"

c := "hoge"

var (
s string
i1, i2, i3 int
)



定数

const a float32 = 0.123

const π flaot64 = 3.14159

const (
win string = "ウィンドウズ"
mac string = "Mac"
)



(+α)定数の一括代入

const (

A int = 1
B
C
)

const (
ZERO = iota
ONE
TWO
THREE
FOUR
)



演算子


  • 算術演算子


    • +, -, *, /, %

    • &, |, ^, &^

    • <<, >>



  • 比較演算子


    • ==, !=, <, <=, >, >=



  • 論理演算子


    • &&, ||, !



  • 受信演算子


    • <-ch





for, if, switch

x := 100

for x > 0 {
x--
fmt.Println("Count: ", x)
}

x := 1

if x > 0 {
fmt.Println(x)
}

i := 3

switch i {
case 1: fmt.Println("hoge")
case 2:fmt.Println("fuga")
case 3: fmt.Println("piyo")
default: fmt.Println("default")
}



(発展) for

// Pattern1

for value := 0;; {
value++
fmt.Println(value)
if value % 10 == 0 {
break
}
}

// Pattern2
sum := 0
for i := 0; i < 10; i++ {
sum += i
fmt.Println(sum)
}

// Pattern3
items := map[int]int{0:10 , 1:20}
for key, value := range items {
fmt.Println("key:", key, " value:", value)
}

// Pattern4
dictionary := map[string]int{
"a": 123,
"b": 456,
"c": 789,
}
for key, value := range dictionary {
fmt.Println("key:", key, " value:", value)
}



関数


  • 宣言方法

  • 可変長パラメータ

  • 名前付き戻り値

  • 関数リテラル



関数 宣言方法

package main

import (
"fmt"
)

func add(x int, y int) int {
return x + y
}

func addAndSub(x, y int) (int, int) {
return x + y, x - y
}

func main() {
x := add(100,200)
fmt.Println(x)
y,z := addAndSub(100,200)
fmt.Println(x,y)
}



関数 可変長パラメータ

package main

import "fmt"

func sample(args ...int) {
fmt.Println(args)
}

func main() {
sample(1, 2, 3, 4)
}

1 [2 3 4]



関数 名前付き戻り値

func f()(dog string, cat string, bird string){

dog = "wan"
cat = "nya-"
bird = "ka-"
return
}

d, c, b := f()



関数リテラル

関数を変数に代入して記述すること

swap := func(a, b int) (int, int) {

return b , a
}
fmt.Println(swap(3, 5))



遅延実行

宣言する関数の終了直前に実行する。よくDBのCloseなどに使用する。

package main

import "fmt"

func main() {
defer fmt.Println("これはmain()関数抜け出す直前に実行")
fmt.Println("この辺は順番に実行")
}



構造体

package main

import "fmt"

type Person struct {
First string
Last string
}

func (p *Person) Name() string {
return p.First + " " + p.Last
}

func main() {
person := &Person{"Taro", "Yamada"}
fmt.Println(person.Name())
}



インタフェース

package main

import "fmt"

type Animal interface {
Cry()
}

type Dog struct {}
func (d *Dog) Cry() {
fmt.Println("わん")
}

type Cat struct {}
func (c *Cat) Cry() {
fmt.Println("にゃー");
}

func do(a Animal) {
a.Cry();
}

func main() {
dog := new(Dog)
cat := new(Cat)
do(dog)
do(cat)
}



配列

// Pattern1

array1 := [5]float64{}

// Pattern2
array2 := [10]int{3,2,4}

// Pattern3
array3 := [...](string){"A", "B", "C"}

// Pattern4
var date[7] string
data[0] = "Sunday"
data[1] = "Monday"



スライス


  • 配列と同様にインデックスでアクセスできる

  • 配列を間接参照

  • スライス自体は要素を持たない

// Pattern1

slice := []int{3,2,4}

// Pattern2
x := [5]string{"a", "b", "c", "d", "e"} // 配列を宣言
var s1 = []string // スライス型の変数を宣言
s1 = x[:] // 配列全体をスライス
fmt.Println(s1)
fmt.Println(s1[1:3])
fmt.Println(s1[2:])



中間演習

構造体のスライスからループで要素を取り出して各要素のメソッドを呼び出して、構造体の名前と名字をスペース区切りで1つの文字列として出力するコードを書こう。


実装方針


  • 名前と名字を要素に持つ構造体を定義

  • main関数の最初に構造体の各要素に具体的な値を代入していく。

  • main関数の最後にループで構造体の各要素を取り出して、構造体のメソッドから名前を出力する関数を呼び出す。

  • 名前を出力する関数


    • 構造体を引数として、名前と名字の2つの値の返り値を返す関数

    • 上で求めた値を使ってスペース区切りで名前と名字をつなげる関数。これが実際に出力される行の出力値



(実行結果: 例)

$ go run name.go

Tarou Tanaka
Jirou Satou
Saburou Suzuki



解答例

終わったら公開。



マップ

他の言語ではハッシュやディクショリナリーと呼ばれるものに近い。

height := make(map[string] int)

height["tanaka"] = 163
height["suzuki"] = 175
height["yamada"] = 183

weight := map[string] int {
"tanaka": 163,
"suzuki": 175,
"yamada": 183,
}
fmt.Println(weight)
fmt.Println(weight["suzuki"])



エラーハンドリング

f, err := os.Open("sample.txt")

if err != nil {
fmt.Println("Error", err.Error())
}

基本的には、例外処理を書かずに、関数に第2引数にエラーがある場合はその要素が値として入るようにする。



パニック

致命的なエラーのためエラーハンドリングをさせる必要がない。もしくはエラーが起きてもリカバリが必要な時。

f, err := os.Open("/tmp/sample.txt")

if err != nil {
panic("Error")
}



リカバリ

package main

import "fmt"

func func1(b bool) {
defer func() {
fmt.Println("defer start.")

if err := recover(); err != nil {
// パニック中だった
fmt.Println("recovery!")
}
fmt.Println("defer end.")
}()
if b {
panic("Occure panic!")
}
}

func main() {
func1(false)
func1(true)
}

出力結果

defer start.

defer end.
defer start.
recovery!
defer end.



中間演習2

以下の条件を満たす関数を作成してmain関数から呼び出そう



  • sample.txtというファイルを新規作成

  • 作成されたテキストファイルに与えられた引数の文字列を書き込む

呼び出し例

func main() {

writeTxt("hoge")
}



Goルーチン


  • チャネル: 作成、クローズ

  • Goは並列処理がめちゃくちゃ簡単に書ける!

package main

import (
"fmt"
"time"
)

func main() {
fmt.Println("Begin.")

// チャネル 作成
channel1 := make(chan bool)
channel2 := make(chan bool)
channel3 := make(chan bool)

go func() {
// 1秒かかるコマンド
fmt.Println("Second1: Start")
time.Sleep(1 * time.Second)
fmt.Println("Second1: End")
channel1 <- true
}()

go func() {
// 2秒かかるコマンド
fmt.Println("Second2: Start")
time.Sleep(2 * time.Second)
fmt.Println("Second2: End")
channel2 <- true
}()

go func() {
// 3秒かかるコマンド
fmt.Println("Second3: Start")
time.Sleep(3 * time.Second)
fmt.Println("Second3: End")
channel3 <- true
}()

// 終わるまで待つ クローズ
<- channel1
<- channel2
<- channel3

fmt.Println("Finished.")
}



最終課題

以下の条件を満たす処理を書こう。


  • 書く処理は並列で処理を行う。


  • sample0.txt, sample1.txt, sample2.txtというファイルを作成する。

  • 上記のファイルのそれぞれにTanaka, Yamada, Suzukiという文字列を書き込む。



解答例

終わってから公開。



快適な環境つくり


  • goファイルはデフォルトでLintが適用されるようにしてしまおう!(Vim ver.)

  • vim上で各種操作をする(NeoBundle ver.)



goファイルはデフォルトでLintが適用されるようにしてしまおう!(Vim ver.)

.vim/after/ftplugin以下に[拡張子].vimというファイルを作り、vimのconfigを書く。

$ mkdir -p ~/.vim/after/ftplugin/

$ vim ~/.vim/after/ftplugin/go.vim


go.vim

set noexpandtab

set tabstop=4
set shiftwidth=4



vim上で各種操作をする(NeoBundle ver.)


インストール

.vimrcに以下のようにvim-go-extraを追記する。

$ vim ~/.vimrc

NeoBundle 'vim-jp/vim-go-extra'

入力したらEscをして以下を入力してインストールする。

:NeoBundleInstall


コマンド


  • :Fmt

  • :Import

  • :Godoc



こんな記事も

vimのGoサポートが手厚くて打ち震えている

http://qiita.com/shiena/items/870ac0f1db8e9a8672a7



(+α) Go言語標準 コマンド 1/2


  • build


    • goファイルをビルド



  • clean


    • 生成ファイルを除去



  • doc


    • ドキュメントを表示



  • env


    • Goで使用されている環境変数を表示。



  • bug



  • fix


    • パッケージにgo tool fixを実行します



  • fmt


    • Lintを適用



  • generate


    • ソースを処理してGoのファイルを生成します





(+α) Go言語標準 コマンド 2/2


  • get


    • ライブラリのダウンロード



  • install


    • パッケージのコンパイルとインストール(依存関係含む)を行います



  • list


    • パッケージのリストを表示します



  • run


    • goファイルを実行(バイナリは生成しない)



  • test


    • テストコードの実行



  • tool


    • 指定したgo toolを実行します



  • version


    • Go言語のバージョンを表示



  • vet


    • コンパイルでは見つけられないヒューリスティックなバグも見つけられることがある。ただし、結果は保証されない。





Go言語 便利コマンド


  • gofmt


    • フォーマットし直してくれる



  • golint


    • Lintツール



  • goimports


    • ソースコード中で使用しているけどインポートしていないライブラリをインポートしてくれる



  • godoc


    • ドキュメントを表示してくれる



  • gorename


    • 変数名や関数名をリネーム



  • guru


    • ソースコードを静的解析



  • gocode


    • コード補完用のエンジンを提供



  • godef


    • 定義ジャンプするためのツール



  • gotags


    • ctags互換のタグを生成





gofmt

フォーマットを適用。

gofmt -w main.go

wオプションを付けないと実行結果を表示するだけでファイルは変化しない。付けると上書きされる。



golint

Lintツール

下記のコマンドでインストール

$ go get -u github.com/golang/lint/golint 

実行

$ golint sample.go



goimports

ソースファイル中で使用しているけど、importされないパッケージをimportする。

下記のコマンドでインストール

$ go get golang.org/x/tools/cmd/goimports 

実行

goimports -w main.go

暗黙的に宣言が必要なライブラリのインポートはできないっぽい。



godef


インストール

$ go get -v github.com/rogpeppe/godef

$ go install -v github.com/rogpeppe/godef

~/.vim/plugin/godef.vimに以下のURL中のソースコードを追加

https://github.com/dgryski/vim-godef/blob/master/plugin/godef.vim

$ mkdir ~/.vim/plugin

$ vim ~/.vim/plugin/godef.vim

.vimrcに以下を追加。デフォルトだと画面が分割されて使いにくい。

let g:godef_split = 0


実行

vimを開いた状態で下記コマンドを実行する。

:Godef <関数名>



godoc

ドキュメントを表示。下記のコマンドでオフラインでもドキュメントが読める。

godoc -http=:8080



Goを活かしたコードを書こう!


  • panic()を使わない。


    • errorを返してエラーチェックを行う。



  • 正規表現を避ける。


    • stringsパッケージを使う。



  • mapを避ける。


    • structでtypeを定義する。



  • reflectを避ける。

  • 巨大なstructを作らない。継承させようとしない。


    • 「継承より移譲」← オブジェクト指向に対する警句

    • 再利用可能な小さな部品を組み合わせてデータ構造を定義する。

    • オブジェクト指向な設計でやるなら埋め込み等の機能を使う。



  • 並行処理を使いすぎない


    • コードの可読性の低下。レースコンディション。



  • テストとCI



    • go vetgolint



  • ビルドとデプロイ


    • go build



  • モニタリング



    • runtimeパッケージ

    • 各種メトリクスを取得して定常的に計測しておくと良い。



みんなのGo言語【現場で使える実践テクニック】 大型本 – 2016/9/9



その他


  • パッケージ管理

  • Webフレームワーク

  • URL集



パッケージ管理


  • dep

  • Glide

  • gom



(+α)パッケージ管理


  • dep

  • Glide

  • gom



  • その他


    • manul

    • Godep

    • Govendor

    • godm

    • vexp

    • gv

    • gvt

    • govend

    • Vendetta

    • trash

    • gsv



(参照)https://github.com/golang/go/wiki/PackageManagementTools



Webフレームワーク


  • Gin

  • Martini

  • Revel



(+α)Webフレームワーク


  • beego

  • bone

  • goji

  • echo

  • その他


    • macaron

    • go-json-rest

    • Bxog

    • pat

    • fasthttprouter

    • lion

    • golf

    • chi

    • httptreemux

    • baa

    • go-ozzo

    • go-restful

    • lars

    • gorilla

    • Gramework

    • gem

    • neo

    • httprouter

    • tango

    • vulcan

    • possum

    • gongular

    • denco

    • traffic

    • ace

    • fasthttp-routing

    • go-tigertonic

    • fasthttp

    • r2router

    • gear

    • possum



(参照) https://github.com/smallnest/go-web-framework-benchmark



URL集



以上



参考

基礎からわかる Go言語 単行本(ソフトカバー) – 2012/11/21

古川 昇 (著)


みんなのGo言語【現場で使える実践テクニック】 大型本 – 2016/9/9