Go
インストール(Ubuntu)
VERSION='go1.12.9'
mkdir -p $HOME/downloads
(cd $HOME/downloads &&
curl -O https://dl.google.com/go/$VERSION.linux-amd64.tar.gz &&
sudo tar -C /usr/local -xzf $VERSION.linux-amd64.tar.gz)
.bashrc
に追記する。
export PATH=$PATH:/usr/local/go/bin
Ref: https://golang.org/doc/install#install
基本
全部ここに書いてある内容と同じ。 https://go-tour-jp.appspot.com/basics
型
type
で任意の型を定義できる。
type MyInt int
var i MyInt = 10
変数
変数定義
var
はパッケージか関数で利用できる。
var a, b, c string
func main() {
var hoge int
}
# 複数の変数ごとに初期化子を書く。型を省略できる
var a, b, c = true, 123, "hoge"
func main() {
# 関数の中なら:=で短くかける
hoge := 1
}
初期値
初期値なしで宣言した変数の初期値。
- 数値型: 0
- bool型: false
- string型: ""
型変換
型(変数)
で型変換。
i := 42
f := float64(a)
u := uint(f)
型推論
:=
やvar =
で型を指定しない場合は右側の変数で型推論される。
i := 42 // int
f := 3.142 // float64
g := 0.867 + 0.5i // complex128
定数
const
を使う。
文字、文字列、boolean、数値でのみ使える。
:=
を使って宣言はできない。
const Hoge = 1
func main() {
const Fuga = "abc"
}
浮動小数点の変数を初期化して宣言する
// どっちでもOK
z := 1.0
z := float64(1)
関数
2つ以上の引数が同じ型の場合は最後の型以外を省略できる
func add(a, b int) int {
return a + b
}
複数の戻り値を返す
func swap(a, b string) (string, string) {
return b, a
}
戻り値の変数に名前をつける(named return value)
戻り値の意味を説明するドキュメントとして使える。
func hoge() (a, b int) {
a = "hoge"
b = "fuga"
return
}
可読性が下がるのでnaked returnは短い関数のときだけ使う。
関数を変数として使う
func(引数) 戻り値の型
で関数を変数として宣言できる。
add := func(a, b int) int {
return a + b
}
fmt.Println(add(1, 2)) // 3
関数を受け取る関数
引数の型をfunc
にすればいい。
func compute(fn func(a, b int) int) int {
return fn(2, 1)
}
func add(a, b int) int {
return a + b
}
func minus(a, b) int {
return a - b
}
func main() {
fmt.Println(compute(add)) // 3
fmt.Println(compute(minus)) // 1
}
クロージャ
関数の中で宣言した変数の状態は、各関数の変数ごとに保持される。
func adder() func(int) int {
sum := 0
return func(x int) int {
sum += x
return sum
}
}
func main() {
pos, neg := adder(), adder()
fmt.Println(pos(2), neg(-2)) // 2 -2
fmt.Println(pos(2), neg(-2)) // 4 -4
fmt.Println(pos(2), neg(-2)) // 6 -6
}
For
変数i
はfor
のスコープ内でのみ有効。
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
初期化と後処理は省略できる。
sum := 1
for ; sum < 1000; {
sum += sum
}
初期化と後処理を省略するときのセミコロンは省略できる。するとwhile
文と同じになる。
sum := 1
for sum < 1000 {
sum += sum
}
すべて省略すると無限ループになる。
for {
}
If
基本的なif
。
if x < 0 {
}
条件と一緒に、if
文のなかでのみ有効な変数を宣言できる。
if v := math.Pow(x, n); v < lim {
return v
} else {
// if文で宣言した変数はelseブロック内でも使える
fmt.Println(v)
}
Switch
各case
に自動的にbreak
が挿入されるのでbreak
を書かなくていい。
if
文と同様に、switch
文を書くときに変数の宣言ができる。
case
は定数、整数でなくてもいい。
import (
"runtime"
)
fmt.Print("Go runs on ")
switch os := runtime.GOOS; os {
case "darwin":
fmt.Println("OS X.")
case "linux":
fmt.Println("Linux.")
default:
// freebsd, openbsd,
// plan9, windows...
fmt.Printf("%s.", os)
}
自動的にbreak
が入るので、次のときi==0
ならf
は呼び出されない。
switch i {
case 0:
case f():
}
if-then-else
が長くなるときは条件なしのswitch
を書くとシンプルになる。
t := time.Now()
switch {
case t.Hour() < 12:
fmt.Println("Good morning!")
case t.Hour() < 17:
fmt.Println("Good afternoon.")
default:
fmt.Println("Good evening.")
}
Defer
defer
へ渡した関数の引数はすぐに評価するが、関数自体の呼び出しは元の関数がreturn
するまで実行されない。
defer fmt.Println("world")
fmt.Println("hello")
// hello world
複数回defer
すると最後のdefer
から呼ばれる。
defer fmt.Println("A")
defer fmt.Println("B")
defer fmt.Println("C")
// C, B, A
defer
はリソースの開放に使える。
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
Ref: https://blog.golang.org/defer-panic-and-recover
ポインタ
T
型の変数のポインタは*T
型で、ゼロ値はnil
。
var p *int
&
演算子で変数のポインタを取得できる。
hoge := 1
fmt.Println(&hoge) // 0x40c128
*
演算子でポインタの指す値を取得できる。
hoge := 1
p := &hoge
fmt.Println(*p) // 1
構造体
複数のフィールドをまとめて構造体として宣言できる。
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
fmt.Println(v) // {1 2}
// フィールドへはドット(.)でアクセスできる
fmt.Println(v.X) // 1
// フィールドは再代入できる
v.X = 4
fmt.Println(v.X) // 4
// ポインタを通してフィールドにアクセスできる
p := &v
(*p).X = 1e9
fmt.println(v) // {1000000000 2}
// (*p).Xの代わりに直接p.Xでも書ける
p.X = 10
fmt.println(v) // {10 2}
}
構造体変数の宣言いろいろ。
v2 = Vertex{X: 1}
のようにName:
構文を使うとき、フィールド指定順序は関係ない。
type Vertex struct {
X, Y int
}
var (
v1 = Vertex{1, 2} // Vertex型
v2 = Vertex{X: 1} // Vertex型。Yは初期値0
v3 = Vertex{} // Vertex型。X, Yともに初期値0
p = &Vertex{1, 2} // *Vertex型。
)
配列
[n]T
型は配列を表す。
// 要素10個のint型配列
var a [10]int
配列の長さは型の一部分で、あとからサイズを変えることはできない。
スライス
可変長の配列。型[]T
で表す。
スライスの生成。配列と違って要素数を指定しないで作れる。
s := []int{2, 3, 5, 7, 11, 13}
スライスのゼロ値はnil
var s []int
fmt.Println(s, len(s), cap(s)) // [] 0 0
if s == nil {
fmt.Println("nil!") // nil!
}
構造体のスライスも作れる。
s := []struct {
a int
b int
}{
{0, 0},
{0, 1},
{1, 0},
{1, 1},
}
a[low:high]
で、配列a
のlow
番目からhigh - 1
番目までの要素をもつスライスを作れる。
primes := [6]int{2, 3, 5, 7, 11, 13}
var s []int = primes[1:4] // 要素1~3を含むスライスを作る
fmt.Println(s)
// [3 5 7]
スライスするときに上限と下限を省略したときは、下限が0、上限はスライスの長さになる。
s := []string{"a", "b", "c", "d", "e"}
fmt.Println(s[0:5]) // [a b c d e]
fmt.Println(s[0:]) // [a b c d e]
fmt.Println(s[:5]) // [a b c d e]
fmt.Println(s[:]) // [a b c d e]
スライスは配列への参照のようなもので、スライスの要素を変更すると元の配列も変更される。
names := [3]string{
"A",
"B",
"C",
}
fmt.Println(names) // [A B C]
a := names[0:2]
a[0] = "XXX"
fmt.Println(a) // [XXX B]
fmt.Println(names) // [XXX B C]
動的サイズの配列を作るには、組み込み関数make
を使う。
make
関数ゼロ化された配列を割り当て、その配列を指すスライスを返す。
a := make([]int, 5) // len(a)=5
// 3番めの引数にスライスの容量を指定できる
b := make([]int, 0, 5) // len(b)=0, cap(b)=5
b = b[:cap(b)] // len(b)=5, cap(b)=5
b = b[1:] // len(b)=4, cap(b)=4
スライスのスライスを作る。
ss := [][]string {
[]string{"a", "b", "c"},
[]string{"d", "e", "f"},
}
ss[0][0] = "A"
スライスに要素を追加する
append
関数を使う。append
関数へは追加元のスライスと追加する要素を渡す。
var s []int
fmt.Println(s) // []
s = append(s, 1)
fmt.Println(s) // [1]
s = append(s, 2, 3, 4)
fmt.Println(s) // [1 2 3 4]
Ref: https://golang.org/pkg/builtin/#append
range: スライスの要素分だけ繰り返す
for
文でrange
を使うと、スライスのインデックスと値を繰り返しごとに返す。
s := []string{"a", "b", "c"}
for i, v := range s {
fmt.Printf("index=%d, value=%d\n", i, v)
}
Map
// map[キー型]バリュー型 で宣言する
var m map[string]int
// ゼロ値はnil
if m == nil {
fmt.Println("nil.")
}
// ゼロ値はキーの追加もできない
m["aaa"] = 1 // error!
// make関数は初期化された使用可能なマップを返す
m = make(map[string]int)
m["hoge"] = 1
fmt.Println(m["hoge"])
// 変数の宣言と同時に初期化する
m := map[string]int {
"aaa": 1,
"bbb": 2,
"ccc": 3,
}
マップの追加・更新
m[key] = elem
マップの要素の取得
elem = m[key]
マップの要素の削除
delete(m, key)
マップに指定した要素が存在するか確認
m[key]
が返す2番めの戻り値で確認できる。
m
にkey
が存在しない場合、elem
はmapの要素型のゼロ値になる。
elem, ok = m[key]
if ok {
// 要素あり
} else {
// 要素なし
}
メソッド
Kotlinの拡張関数のような感じ。 既存の型にメソッドを定義できる。
type Point struct {
X, Y int
}
func (p Point) Move(x, y int) Point {
return Point{ p.X + x, p.Y + y}
}
func main() {
p := Point{10, 20}
fmt.Println(p.Move(2, 3)) // {12, 23}
}
任意の型にメソッドを宣言する
type
とメソッドを組み合わせると、次のように実質同じ型(int
どうし)なのにMyInt
として宣言した変数しかAdd
関数を呼べない、といったコードが書ける。
type MyInt int
func (a MyInt) Add() MyInt {
return a + 10
}
func main() {
var i MyInt = 1
fmt.Println(i.Add()) // 11
var ii int = 1
fmt.Println(ii.Add()) // error! int型にAdd関数はない
}
ポインタレシーバを使ってメソッド内でレシーバ変数を変更する
メソッド引数の型をポインタ(*
)にすると、引数のフィールドをメソッド内で変更できるようになる。
変数レシーバの場合、変数のコピーが引数に渡されるため、引数のフィールドを変更できない。
ポインタレシーバを使う場合、基本的にレシーバのnilチェックをする。
type Point struct {
X, Y int
}
// ポインタレシーバ
func (p *Point) Move(x, y int) {
if p == nil {
// nilチェック
fmt.Println("<nil>")
return
}
p.X = p.X + x
p.Y = p.Y + y
}
func main() {
p := Point{0, 0}
p.Move(1, 2)
fmt.Println(p) // {1 2}
}
インタフェース
インタフェース型はメソッドシグニチャの集まりで定義する。
type Hoge interface {
Func() int
}
var a Hoge
空のインタフェース
interface{}
で空のインタフェースを定義できる。
TypeScriptのany
型みたいなもの。
func describe(i interface{}) {
fmt.Println(i)
}
型アサーション
変数名.(型名)
で型アサーションができる。
1番目の戻り値が型変換済みの変数、2番めの戻り値は型アサーション結果(true
or false
)が入る。
型アサーションに失敗した場合は1番目の戻り値にゼロ値が入る。
2番目の戻り値を省略して型アサーションに失敗するとpanicが起きる。
var i interface{} = "hello"
s := i.(string) // s=hello
s, ok := i.(float64) // s=0, ok=false
s := i.(float64) // panic
Tips
byte -> string変換
var b byte = 127
s := fmt.Sprintf("%d", b)
// s="127"
Switchで型判定
変数名.(type)
を使う。
func do(i interface{}) {
switch v := i.(type) {
case int:
fmt.Println("i is int")
case string
fmt.Println("i is string")
default :
fmt.Println("others...")
}
}
文字列を単語(スペース)区切のスライスに変換する
strings.Fields()
を使う。
import "strings"
s = strings.Fields("A man a plan a canal panama")
// s=[A man a plan a canal panama]
型名を表示する
%T
を使う。
v := 10
fmt.Printf("v is of type %T\n", v)
HelloWorld
main.go
を書く。
package main
import "fmt"
func main() {
fmt.Println("Hello, 世界")
}
go run
で実行する。
go run main.go
GAE/GO でHelloWorld
ファイル構成。
app.yaml
hello.go
app.yaml
を書く。
runtime: go
api_version: go1
handlers:
- url: /.*
script: _go_app
package main
import (
"fmt"
"net/http"
"google.golang.org/appengine"
)
func main() {
http.HandleFunc("/", handle)
appengine.Main()
}
func handle(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello, world!")
}
ローカル環境で動作確認するには次のコマンドを実行する。
curl localhost:8080/
でつながる。
ソースを変更するとアプリが再コンパイルされて起動される。
dev_appserver.py app.yaml
GAEにデプロイするには次のコマンドを実行する。
gcloud app deploy
Ref: https://cloud.google.com/appengine/docs/standard/go/quickstart?hl=ja#test_the_application
FAQ
go getでエラー
~/go
を消してもう一度go get
する。
← CSS Javascript →